@rhi-zone/rainbow 0.1.0 → 0.2.0-alpha.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/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # @rhi-zone/rainbow
2
+
3
+ Optics-based reactivity for TypeScript. Seven composable primitives, no framework coupling, no magic.
4
+
5
+ ## What it is
6
+
7
+ Rainbow gives you structured state management grounded in the algebra of optics. Instead of scattered reactive variables or a centralized store with action boilerplate, you get:
8
+
9
+ - **Signals** — reactive cells with `get`, `set`, `subscribe`, and `map`.
10
+ - **Lenses** — focus on a field of a record, composable and law-abiding.
11
+ - **Prisms** — focus on a case of a sum type (discriminated unions, optionals).
12
+ - **Traversals** — focus on zero or more values within a collection.
13
+ - **Computed** — derive from multiple sources with an explicit dep list.
14
+ - **Cond** — conditional propagation, composable like `&&`.
15
+ - **Product** — pair two signals into one `Signal<[A, B]>`.
16
+
17
+ Everything is a plain TypeScript value. No decorators, no global state, no framework requirement.
18
+
19
+ ## Install
20
+
21
+ ```sh
22
+ npm install @rhi-zone/rainbow
23
+ ```
24
+
25
+ ## Quick example
26
+
27
+ ```ts
28
+ import { signal, field, computed } from '@rhi-zone/rainbow'
29
+
30
+ type State = { user: { name: string }; count: number }
31
+
32
+ const state = signal<State>({ user: { name: 'Alice' }, count: 0 })
33
+
34
+ // Focus on a nested field — reads and writes go through the lens
35
+ const name = state.focus(field('user')).focus(field('name'))
36
+
37
+ name.get() // 'Alice'
38
+ name.set('Bob')
39
+ state.get() // { user: { name: 'Bob' }, count: 0 }
40
+
41
+ // Derive from multiple sources
42
+ const summary = computed(
43
+ () => `${name.get()} has visited ${state.focus(field('count')).get()} times`,
44
+ [name, state],
45
+ )
46
+ summary.get() // 'Bob has visited 0 times'
47
+ ```
48
+
49
+ ## Docs
50
+
51
+ Full guides, API reference, and design notes are at the [Rainbow VitePress site](https://rhi.zone/rainbow/).
@@ -1,3 +1,4 @@
1
+ import type { Signal, ReadonlySignal } from './signal.ts';
1
2
  /**
2
3
  * AsyncData<T, E> — the type of an asynchronous value.
3
4
  *
@@ -21,20 +22,28 @@ export type AsyncData<T, E = unknown> = {
21
22
  readonly status: 'success';
22
23
  readonly value: T;
23
24
  };
25
+ /** Singleton for the "not yet requested" state. */
24
26
  export declare const notAsked: AsyncData<never, never>;
27
+ /** Singleton for the "request in flight" state. */
25
28
  export declare const loading: AsyncData<never, never>;
29
+ /** Construct a failure value. */
26
30
  export declare const failure: <E>(error: E) => AsyncData<never, E>;
31
+ /** Construct a success value. */
27
32
  export declare const success: <T>(value: T) => AsyncData<T, never>;
33
+ /** Narrowing guard for the `notAsked` state. */
28
34
  export declare const isNotAsked: <T, E>(ad: AsyncData<T, E>) => ad is {
29
35
  status: 'notAsked';
30
36
  };
37
+ /** Narrowing guard for the `loading` state. */
31
38
  export declare const isLoading: <T, E>(ad: AsyncData<T, E>) => ad is {
32
39
  status: 'loading';
33
40
  };
41
+ /** Narrowing guard for the `failure` state. */
34
42
  export declare const isFailure: <T, E>(ad: AsyncData<T, E>) => ad is {
35
43
  status: 'failure';
36
44
  error: E;
37
45
  };
46
+ /** Narrowing guard for the `success` state. */
38
47
  export declare const isSuccess: <T, E>(ad: AsyncData<T, E>) => ad is {
39
48
  status: 'success';
40
49
  value: T;
@@ -45,12 +54,81 @@ export declare const map: <T, U, E>(ad: AsyncData<T, E>, f: (value: T) => U) =>
45
54
  export declare const mapError: <T, E, F>(ad: AsyncData<T, E>, f: (error: E) => F) => AsyncData<T, F>;
46
55
  /** Chain async operations — flatMap over the success case. */
47
56
  export declare const chain: <T, U, E>(ad: AsyncData<T, E>, f: (value: T) => AsyncData<U, E>) => AsyncData<U, E>;
48
- /** Unwrap with a fallback for non-success states. */
57
+ /**
58
+ * Unwrap the success value, or return `fallback` for all other states.
59
+ * @param fallback - Value to return when not in the success state.
60
+ */
49
61
  export declare const getOrElse: <T, E>(ad: AsyncData<T, E>, fallback: T) => T;
50
- /** Fold over all four states. */
62
+ /**
63
+ * Fold over all four states.
64
+ * @param cases - Handlers for each state; all four must be provided.
65
+ */
51
66
  export declare const fold: <T, E, R>(ad: AsyncData<T, E>, cases: {
52
67
  notAsked: () => R;
53
68
  loading: () => R;
54
69
  failure: (error: E) => R;
55
70
  success: (value: T) => R;
56
71
  }) => R;
72
+ /**
73
+ * Wrap an already-in-flight promise as a `ReadonlySignal<AsyncData<T, E>>`.
74
+ * Starts in `loading`; transitions to `success` or `failure` when the promise
75
+ * settles. The signal is read-only — it can only be updated by the promise.
76
+ *
77
+ * @example
78
+ * const user = fromPromise(fetchUser(id))
79
+ * // user.get() === loading initially, then success({ ... }) or failure(e)
80
+ */
81
+ export declare function fromPromise<T, E = unknown>(promise: Promise<T>): ReadonlySignal<AsyncData<T, E>>;
82
+ /**
83
+ * Derive a `ReadonlySignal<AsyncData<T, E>>` that re-runs `fn` whenever
84
+ * `deps` changes. Each new run receives a fresh `AbortSignal`; the previous
85
+ * in-flight request is aborted before the next one starts. Starts in
86
+ * `loading` immediately.
87
+ *
88
+ * Returns `[signal, dispose]`. Call `dispose()` when done — it aborts any
89
+ * in-flight request and unsubscribes from `deps`. In a widget context, pass
90
+ * `dispose` to `register()` so it is called automatically on unmount.
91
+ *
92
+ * @example
93
+ * const [results, dispose] = fromAsync(querySignal, (q, abort) =>
94
+ * fetch(`/api/search?q=${q}`, { signal: abort }).then(r => r.json())
95
+ * )
96
+ * register(dispose) // clean up when widget unmounts
97
+ */
98
+ export declare function fromAsync<D, T, E = unknown>(deps: ReadonlySignal<D>, fn: (deps: D, abort: AbortSignal) => Promise<T>): [ReadonlySignal<AsyncData<T, E>>, dispose: () => void];
99
+ /**
100
+ * Create an imperatively-triggered async operation.
101
+ *
102
+ * Unlike `fromAsync`, this is not driven by a signal — you call `trigger(input)`
103
+ * manually (e.g. from a button handler). Concurrent calls abort the previous
104
+ * in-flight request via AbortSignal.
105
+ *
106
+ * @returns
107
+ * `result` — ReadonlySignal<AsyncData<T, E>> starting as notAsked()
108
+ * `trigger` — Call with input to start the async operation
109
+ * `dispose` — Cancel any in-flight request and stop updates
110
+ *
111
+ * @example
112
+ * const { result, trigger, dispose } = fromAsyncImperative(
113
+ * async (contact: Contact, signal) => await saveContact(contact, signal)
114
+ * )
115
+ * on(saveBtn, "click", () => trigger(formState.get().values))
116
+ * register(dispose)
117
+ */
118
+ export declare function fromAsyncImperative<I, T, E = unknown>(fn: (input: I, signal: AbortSignal) => Promise<T>): {
119
+ readonly result: ReadonlySignal<AsyncData<T, E>>;
120
+ readonly trigger: (input: I) => void;
121
+ readonly dispose: () => void;
122
+ };
123
+ /**
124
+ * Returns a Promise that resolves with the next value from `s` that satisfies
125
+ * `predicate`. If the current value already satisfies it, resolves immediately.
126
+ *
127
+ * Useful for bridging signal-based async results back to imperative await:
128
+ *
129
+ * @example
130
+ * trigger(contact)
131
+ * const result = await toNextValue(saveResult, r => !isLoading(r))
132
+ * if (isSuccess(result)) ...
133
+ */
134
+ export declare function toNextValue<T>(s: Signal<T> | ReadonlySignal<T>, predicate?: (v: T) => boolean): Promise<T>;
@@ -2,7 +2,10 @@ import type { ReadonlySignal } from './signal.ts';
2
2
  /**
3
3
  * Derive a read-only signal from multiple source signals.
4
4
  *
5
- * Unlike signal.map (one source), computed accepts any number of sources
6
- * and recomputes when any of them change.
5
+ * Unlike `signal.map` (one source), `computed` accepts any number of sources
6
+ * and recomputes `fn` when any of them change.
7
+ *
8
+ * @param fn - Pure function that reads from one or more signals and returns the derived value.
9
+ * @param deps - Explicit dependency list; the signal re-evaluates when any dep changes.
7
10
  */
8
11
  export declare function computed<T>(fn: () => T, deps: ReadonlySignal<unknown>[]): ReadonlySignal<T>;
package/dist/cond.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ReadonlySignal } from './signal.ts';
2
2
  /**
3
- * Conditional signal — propagates the value when `pred` holds, undefined otherwise.
3
+ * Conditional signal — propagates the value when `pred` holds, `undefined` otherwise.
4
4
  *
5
5
  * Accepts both `ReadonlySignal<A>` and `ReadonlySignal<A | undefined>` so that
6
6
  * cond calls can be composed directly:
@@ -10,7 +10,7 @@ import type { ReadonlySignal } from './signal.ts';
10
10
  * When the source already carries `undefined` (from a prior `cond` or `narrow`),
11
11
  * undefined passes through unchanged — equivalent to short-circuit `&&`.
12
12
  *
13
- * Usage:
13
+ * @example
14
14
  * const positiveCount = cond(n => n > 0, countSignal)
15
15
  * const positiveEven = cond(n => n % 2 === 0, positiveCount)
16
16
  */
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
+ export type { Optic } from './optic.ts';
1
2
  export type { Lens } from './lens.ts';
2
- export { lens, composeLens, field, fst, snd, id } from './lens.ts';
3
+ export { lens, composeLens, field, index, id, arrayOf, recordOf, mapOf } from './lens.ts';
3
4
  export type { Prism } from './prism.ts';
4
- export { prism, composePrism, some, iso } from './prism.ts';
5
+ export { prism, composePrism, some, iso, guard, nullable, tagged } from './prism.ts';
5
6
  export type { Signal, ReadonlySignal } from './signal.ts';
6
7
  export { signal, batch } from './signal.ts';
7
8
  export { computed } from './computed.ts';
@@ -10,4 +11,4 @@ export { product, stateful } from './product.ts';
10
11
  export type { Traversal } from './traversal.ts';
11
12
  export { traversal, each, filtered, nth, composeWithLens, composeTraversal } from './traversal.ts';
12
13
  export type { AsyncData } from './async-data.ts';
13
- export { notAsked, loading, failure, success, isNotAsked, isLoading, isFailure, isSuccess, map as mapAsyncData, mapError, chain as chainAsyncData, getOrElse, fold, } from './async-data.ts';
14
+ export { notAsked, loading, failure, success, isNotAsked, isLoading, isFailure, isSuccess, map as mapAsyncData, mapError, chain as chainAsyncData, getOrElse, fold, fromPromise, fromAsync, fromAsyncImperative, toNextValue, } from './async-data.ts';
package/dist/lens.d.ts CHANGED
@@ -1,22 +1,60 @@
1
+ import type { Optic } from './optic.ts';
1
2
  /**
2
3
  * A Lens<A, B> focuses on a field of type B within a structure of type A.
3
4
  *
4
5
  * Laws:
5
- * get(set(a, b)) = b
6
- * set(a, get(a)) = a
7
- * set(set(a, b1), b2) = set(a, b2)
6
+ * view(review(b, a)) = b
7
+ * review(view(a), a) = a
8
+ * review(b, review(b1, a)) = review(b, a)
8
9
  */
9
- export interface Lens<A, B> {
10
- get(a: A): B;
11
- set(a: A, b: B): A;
10
+ export interface Lens<A, B> extends Optic<A, B> {
11
+ view(a: A): B;
12
+ review(b: B, a: A): A;
12
13
  }
13
- export declare function lens<A, B>(get: (a: A) => B, set: (a: A, b: B) => A): Lens<A, B>;
14
+ /**
15
+ * Construct a lens from explicit view and review functions.
16
+ * @param view - Extract B from A.
17
+ * @param review - Return a new A with B replaced.
18
+ */
19
+ export declare function lens<A, B>(view: (a: A) => B, review: (b: B, a: A) => A): Lens<A, B>;
20
+ /**
21
+ * Compose two lenses. If `ab` focuses on B within A, and `bc` focuses on C within B,
22
+ * the result focuses on C within A.
23
+ */
14
24
  export declare function composeLens<A, B, C>(ab: Lens<A, B>, bc: Lens<B, C>): Lens<A, C>;
15
- /** Lens into the first element of a tuple */
16
- export declare const fst: <A, B>() => Lens<[A, B], A>;
17
- /** Lens into the second element of a tuple */
18
- export declare const snd: <A, B>() => Lens<[A, B], B>;
19
- /** Lens into a record field */
25
+ /** Lens into a tuple element by index. `index(0)` replaces `fst`, `index(1)` replaces `snd`. */
26
+ export declare function index<T extends readonly unknown[], N extends number & keyof T>(n: N): Lens<T, T[N]>;
27
+ /**
28
+ * Lens into a record field by key.
29
+ * @param key - The key of the field to focus on.
30
+ */
20
31
  export declare function field<A, K extends keyof A>(key: K): Lens<A, A[K]>;
21
- /** Identity lens */
32
+ /** Identity lens — focuses on the whole value. */
22
33
  export declare function id<A>(): Lens<A, A>;
34
+ /**
35
+ * Lift a `Lens<S, A>` to `Lens<S[], A[]>`, applying it pointwise over an array.
36
+ *
37
+ * Laws hold as long as the `A[]` written back has the same length as the `S[]`
38
+ * it came from — which is guaranteed when the only source of `A[]` values is
39
+ * `view` on the same array.
40
+ *
41
+ * Useful for two-way bindings over arrays of objects:
42
+ * signal<User[]>.focus(arrayOf(field("name"))) // Signal<string[]>
43
+ */
44
+ export declare function arrayOf<S, A>(l: Lens<S, A>): Lens<S[], A[]>;
45
+ /**
46
+ * Lift a `Lens<S, A>` to `Lens<Record<K, S>, Record<K, A>>`, applying it to
47
+ * every value in the record.
48
+ *
49
+ * Useful for normalized state keyed by branded IDs:
50
+ * signal<Record<UserId, User>>.focus(recordOf(field("name"))) // Signal<Record<UserId, string>>
51
+ */
52
+ export declare function recordOf<K extends string, S, A>(l: Lens<S, A>): Lens<Record<K, S>, Record<K, A>>;
53
+ /**
54
+ * Lift a `Lens<S, A>` to `Lens<Map<K, S>, Map<K, A>>`, applying it to every
55
+ * value in the map.
56
+ *
57
+ * Prefer this over `recordOf` when keys are arbitrary strings — `Map` avoids
58
+ * prototype-pollution footguns with special keys like `__proto__`.
59
+ */
60
+ export declare function mapOf<K, S, A>(l: Lens<S, A>): Lens<Map<K, S>, Map<K, A>>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared supertype for Lens and Prism.
3
+ *
4
+ * - `view` — extract B from A; may return undefined (Prism) or always
5
+ * succeeds (Lens, which narrows the return to B, not B|undefined).
6
+ * - `review` — embed B back into A. Prism implements this as `(b) => A`,
7
+ * satisfying the `(b, a) => A` signature because TypeScript
8
+ * allows fewer parameters. Lens uses both `b` and `a` to
9
+ * preserve surrounding structure.
10
+ *
11
+ * Use `Optic<A, B>` at call sites that accept either optic kind.
12
+ */
13
+ export interface Optic<A, B> {
14
+ view(a: A): B | undefined;
15
+ review(b: B, a: A): A;
16
+ }
package/dist/prism.d.ts CHANGED
@@ -1,17 +1,63 @@
1
+ import type { Optic } from './optic.ts';
1
2
  /**
2
3
  * A Prism<A, B> focuses on a case of type B within a sum type A.
3
4
  *
4
5
  * Laws:
5
- * match(inject(b)) = b
6
- * if match(a) = b then inject(b) = a
6
+ * view(review(b)) = b
7
+ * if view(a) = b then review(b) = a
7
8
  */
8
- export interface Prism<A, B> {
9
- match(a: A): B | undefined;
10
- inject(b: B): A;
9
+ export interface Prism<A, B> extends Optic<A, B> {
10
+ view(a: A): B | undefined;
11
+ review(b: B): A;
11
12
  }
12
- export declare function prism<A, B>(match: (a: A) => B | undefined, inject: (b: B) => A): Prism<A, B>;
13
+ /**
14
+ * Construct a prism from explicit view and review functions.
15
+ * @param view - Extract B from A, or return undefined if the case doesn't apply.
16
+ * @param review - Construct an A from a B.
17
+ */
18
+ export declare function prism<A, B>(view: (a: A) => B | undefined, review: (b: B) => A): Prism<A, B>;
19
+ /**
20
+ * Compose two prisms. If `ab` focuses on B within A, and `bc` focuses on C within B,
21
+ * the result focuses on C within A.
22
+ */
13
23
  export declare function composePrism<A, B, C>(ab: Prism<A, B>, bc: Prism<B, C>): Prism<A, C>;
14
24
  /** Prism for the Some case of an optional value */
15
25
  export declare function some<A>(): Prism<A | undefined, A>;
16
- /** Prism that always matches (isomorphism) */
26
+ /**
27
+ * Prism that always matches — models an isomorphism between A and B.
28
+ * @param to - Convert A to B.
29
+ * @param from - Convert B back to A.
30
+ */
17
31
  export declare function iso<A, B>(to: (a: A) => B, from: (b: B) => A): Prism<A, B>;
32
+ /**
33
+ * Prism from a type predicate. The most general constructor — `some` and
34
+ * `nullable` are both special cases.
35
+ *
36
+ * @example
37
+ * const positive = guard((n: number): n is number => n > 0)
38
+ * signal(signal.narrow(positive)) // Signal<number | undefined>
39
+ */
40
+ export declare function guard<A, B extends A>(predicate: (a: A) => a is B): Prism<A, B>;
41
+ /**
42
+ * Prism for the non-null case. Use `some` for `T | undefined`,
43
+ * `nullable` for `T | null`.
44
+ */
45
+ export declare function nullable<A>(): Prism<A | null, A>;
46
+ /**
47
+ * Prism for a specific variant of a discriminated union, matched by a tag field.
48
+ *
49
+ * `A` cannot be inferred from the key/value arguments alone — provide it
50
+ * explicitly when not in a `.narrow()` context where it can be inferred:
51
+ *
52
+ * @example
53
+ * type Shape = { kind: "circle"; r: number } | { kind: "rect"; w: number; h: number }
54
+ *
55
+ * // In a narrow() call — A inferred from the signal's type:
56
+ * shapeSignal.narrow(tagged("kind", "circle"))
57
+ * // Signal<{ kind: "circle"; r: number } | undefined>
58
+ *
59
+ * // Standalone — provide A explicitly (K and V are inferred from the args):
60
+ * const circle = tagged<Shape>("kind", "circle")
61
+ * // Prism<Shape, { kind: "circle"; r: number }>
62
+ */
63
+ export declare function tagged<A, K extends keyof A, V extends A[K]>(key: K, value: V): Prism<A, Extract<A, Record<K, V>>>;
package/dist/product.d.ts CHANGED
@@ -1,19 +1,24 @@
1
1
  import type { Signal } from './signal.ts';
2
2
  /**
3
- * Create a signal over a pair [A, B] backed by two independent signals.
3
+ * Create a signal over a pair `[A, B]` backed by two independent signals.
4
+ * Writes to the product signal are atomically batched across both children.
5
+ *
6
+ * @param a - Signal for the first element.
7
+ * @param b - Signal for the second element.
4
8
  */
5
9
  export declare function product<A, B>(a: Signal<A>, b: Signal<B>): Signal<[A, B]>;
6
10
  /**
7
- * Encapsulate internal state S alongside external signal A.
11
+ * Attach a local state `S` to an external signal `A`, returning `Signal<[S, A]>`.
8
12
  *
9
- * The S state starts as `init` and lives locally — not accessible
10
- * from outside except via the returned signal's focus(fst()) / focus(snd()).
13
+ * The `S` state starts as `init` and lives locally — not observable
14
+ * from outside except via `.focus(index(0))` / `.focus(index(1))`.
11
15
  *
12
- * Unicorn equivalent: `stateful init widget`
16
+ * @param init - Initial value for the local state.
17
+ * @param outer - The external signal to pair with.
13
18
  *
14
- * Usage:
15
- * const combined = stateful("", itemsSignal) // Signal<[string, Item[]]>
16
- * const draft = combined.focus(fst()) // Signal<string> — local
17
- * const items = combined.focus(snd()) // Signal<Item[]> — external
19
+ * @example
20
+ * const combined = stateful("", itemsSignal) // Signal<[string, Item[]]>
21
+ * const draft = combined.focus(index(0)) // Signal<string> — local
22
+ * const items = combined.focus(index(1)) // Signal<Item[]> — external
18
23
  */
19
24
  export declare function stateful<S, A>(init: S, outer: Signal<A>): Signal<[S, A]>;
package/dist/rainbow.js CHANGED
@@ -1,62 +1,107 @@
1
- var d = Object.defineProperty;
2
- var w = (s, t, e) => t in s ? d(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
3
- var u = (s, t, e) => w(s, typeof t != "symbol" ? t + "" : t, e);
4
- function _(s, t) {
5
- return { get: s, set: t };
1
+ var j = Object.defineProperty;
2
+ var S = (e, t, s) => t in e ? j(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s;
3
+ var u = (e, t, s) => S(e, typeof t != "symbol" ? t + "" : t, s);
4
+ function a(e, t) {
5
+ return { view: e, review: t };
6
6
  }
7
- function P(s, t) {
7
+ function F(e, t) {
8
8
  return {
9
- get: (e) => t.get(s.get(e)),
10
- set: (e, r) => s.set(e, t.set(s.get(e), r))
9
+ view: (s) => t.view(e.view(s)),
10
+ review: (s, r) => e.review(t.review(s, e.view(r)), r)
11
11
  };
12
12
  }
13
- const z = () => _(([s]) => s, ([, s], t) => [t, s]), C = () => _(([, s]) => s, ([s], t) => [s, t]);
14
- function F(s) {
15
- return _(
16
- (t) => t[s],
17
- (t, e) => ({ ...t, [s]: e })
13
+ function I(e) {
14
+ return a(
15
+ (t) => t[e],
16
+ (t, s) => {
17
+ const r = [...s];
18
+ return r[e] = t, r;
19
+ }
18
20
  );
19
21
  }
20
- function N() {
21
- return _((s) => s, (s, t) => t);
22
+ function R(e) {
23
+ return a(
24
+ (t) => t[e],
25
+ (t, s) => ({ ...s, [e]: t })
26
+ );
27
+ }
28
+ function T() {
29
+ return a((e) => e, (e) => e);
30
+ }
31
+ function V(e) {
32
+ return a(
33
+ (t) => t.map((s) => e.view(s)),
34
+ (t, s) => s.map((r, n) => e.review(t[n], r))
35
+ );
36
+ }
37
+ function W(e) {
38
+ return a(
39
+ (t) => Object.fromEntries(Object.entries(t).map(([s, r]) => [s, e.view(r)])),
40
+ (t, s) => Object.fromEntries(Object.entries(s).map(([r, n]) => [r, e.review(t[r], n)]))
41
+ );
22
42
  }
23
- function p(s, t) {
24
- return { match: s, inject: t };
43
+ function q(e) {
44
+ return a(
45
+ (t) => new Map([...t].map(([s, r]) => [s, e.view(r)])),
46
+ (t, s) => new Map([...s].map(([r, n]) => [r, e.review(t.get(r), n)]))
47
+ );
25
48
  }
26
- function R(s, t) {
49
+ function f(e, t) {
50
+ return { view: e, review: t };
51
+ }
52
+ function B(e, t) {
27
53
  return {
28
- match: (e) => {
29
- const r = s.match(e);
30
- return r !== void 0 ? t.match(r) : void 0;
54
+ view: (s) => {
55
+ const r = e.view(s);
56
+ return r !== void 0 ? t.view(r) : void 0;
31
57
  },
32
- inject: (e) => s.inject(t.inject(e))
58
+ review: (s) => e.review(t.review(s))
33
59
  };
34
60
  }
35
- function T() {
36
- return p(
37
- (s) => s,
38
- (s) => s
61
+ function G() {
62
+ return f(
63
+ (e) => e,
64
+ (e) => e
65
+ );
66
+ }
67
+ function H(e, t) {
68
+ return f(e, t);
69
+ }
70
+ function J(e) {
71
+ return f(
72
+ (t) => e(t) ? t : void 0,
73
+ (t) => t
39
74
  );
40
75
  }
41
- function W(s, t) {
42
- return p(s, t);
76
+ function K() {
77
+ return f(
78
+ (e) => e !== null ? e : void 0,
79
+ (e) => e
80
+ );
81
+ }
82
+ function Q(e, t) {
83
+ return f(
84
+ (s) => s[e] === t ? s : void 0,
85
+ // Extract<A, Record<K,V>> extends A, so this upcast is safe
86
+ (s) => s
87
+ );
43
88
  }
44
- let l = 0;
45
- const f = /* @__PURE__ */ new Map();
46
- function v(s) {
47
- l++;
89
+ let p = 0;
90
+ const _ = /* @__PURE__ */ new Map();
91
+ function k(e) {
92
+ p++;
48
93
  try {
49
- s();
94
+ e();
50
95
  } finally {
51
- if (l--, l === 0)
52
- for (; f.size > 0; ) {
53
- const t = [...f.values()];
54
- f.clear();
55
- for (const e of t) e();
96
+ if (p--, p === 0)
97
+ for (; _.size > 0; ) {
98
+ const t = [..._.values()];
99
+ _.clear();
100
+ for (const s of t) s();
56
101
  }
57
102
  }
58
103
  }
59
- class A {
104
+ class x {
60
105
  constructor(t) {
61
106
  u(this, "_value");
62
107
  u(this, "_subscribers", /* @__PURE__ */ new Set());
@@ -67,292 +112,356 @@ class A {
67
112
  }
68
113
  set(t) {
69
114
  if (!Object.is(this._value, t))
70
- if (this._value = t, l > 0)
71
- for (const e of this._subscribers)
72
- f.set(e, () => e(this._value));
115
+ if (this._value = t, p > 0)
116
+ for (const s of this._subscribers)
117
+ _.set(s, () => s(this._value));
73
118
  else
74
- for (const e of this._subscribers) e(t);
119
+ for (const s of this._subscribers) s(t);
75
120
  }
76
121
  subscribe(t) {
77
122
  return this._subscribers.add(t), () => this._subscribers.delete(t);
78
123
  }
79
124
  map(t) {
80
- return new c(this, t);
125
+ return new h(this, t);
81
126
  }
82
127
  focus(t) {
83
- return new o(this, t);
128
+ return new b(this, t);
84
129
  }
85
130
  narrow(t) {
86
- return new a(this, t);
131
+ return new w(this, t);
87
132
  }
88
133
  }
89
- class c {
90
- constructor(t, e) {
134
+ class h {
135
+ constructor(t, s) {
91
136
  u(this, "_source");
92
137
  u(this, "_f");
93
- this._source = t, this._f = e;
138
+ this._source = t, this._f = s;
94
139
  }
95
140
  get() {
96
141
  return this._f(this._source.get());
97
142
  }
98
143
  subscribe(t) {
99
- let e = this.get();
144
+ let s = this.get();
100
145
  return this._source.subscribe(() => {
101
146
  const r = this.get();
102
- Object.is(e, r) || (e = r, t(r));
147
+ Object.is(s, r) || (s = r, t(r));
103
148
  });
104
149
  }
105
150
  map(t) {
106
- return new c(this, t);
151
+ return new h(this, t);
107
152
  }
108
153
  }
109
- class o {
110
- constructor(t, e) {
154
+ class b {
155
+ constructor(t, s) {
111
156
  u(this, "_source");
112
157
  u(this, "_lens");
113
- this._source = t, this._lens = e;
158
+ this._source = t, this._lens = s;
114
159
  }
115
160
  get() {
116
- return this._lens.get(this._source.get());
161
+ return this._lens.view(this._source.get());
117
162
  }
118
163
  set(t) {
119
- this._source.set(this._lens.set(this._source.get(), t));
164
+ this._source.set(this._lens.review(t, this._source.get()));
120
165
  }
121
166
  subscribe(t) {
122
- let e = this.get();
167
+ let s = this.get();
123
168
  return this._source.subscribe(() => {
124
169
  const r = this.get();
125
- Object.is(e, r) || (e = r, t(r));
170
+ Object.is(s, r) || (s = r, t(r));
126
171
  });
127
172
  }
128
173
  map(t) {
129
- return new c(this, t);
174
+ return new h(this, t);
130
175
  }
131
176
  focus(t) {
132
- return new o(this, t);
177
+ return new b(this, t);
133
178
  }
134
179
  narrow(t) {
135
- return new a(this, t);
180
+ return new w(this, t);
136
181
  }
137
182
  }
138
- class a {
139
- constructor(t, e) {
183
+ class w {
184
+ constructor(t, s) {
140
185
  u(this, "_source");
141
186
  u(this, "_prism");
142
- this._source = t, this._prism = e;
187
+ this._source = t, this._prism = s;
143
188
  }
144
189
  get() {
145
- return this._prism.match(this._source.get());
190
+ return this._prism.view(this._source.get());
146
191
  }
147
192
  set(t) {
148
- t !== void 0 && this._source.set(this._prism.inject(t));
193
+ t !== void 0 && this._source.set(this._prism.review(t));
149
194
  }
150
195
  subscribe(t) {
151
- let e = this.get();
196
+ let s = this.get();
152
197
  return this._source.subscribe(() => {
153
198
  const r = this.get();
154
- Object.is(e, r) || (e = r, t(r));
199
+ Object.is(s, r) || (s = r, t(r));
155
200
  });
156
201
  }
157
202
  map(t) {
158
- return new c(this, t);
203
+ return new h(this, t);
159
204
  }
160
205
  focus(t) {
161
- return new o(this, t);
206
+ return new b(this, t);
162
207
  }
163
208
  narrow(t) {
164
- return new a(this, t);
209
+ return new w(this, t);
165
210
  }
166
211
  }
167
- function m(s) {
168
- return new A(s);
212
+ function g(e) {
213
+ return new x(e);
169
214
  }
170
- function j(s, t) {
171
- return new o(s, t);
215
+ function E(e, t) {
216
+ return new b(e, t);
172
217
  }
173
- function k(s, t) {
174
- return new a(s, t);
218
+ function P(e, t) {
219
+ return new w(e, t);
175
220
  }
176
- function y(s, t) {
177
- return new O(s, t);
221
+ function C(e, t) {
222
+ return new M(e, t);
178
223
  }
179
- class O {
180
- constructor(t, e) {
224
+ class M {
225
+ constructor(t, s) {
181
226
  u(this, "_fn");
182
227
  u(this, "_deps");
183
- this._fn = t, this._deps = e;
228
+ this._fn = t, this._deps = s;
184
229
  }
185
230
  get() {
186
231
  return this._fn();
187
232
  }
188
233
  subscribe(t) {
189
- let e = this.get();
234
+ let s = this.get();
190
235
  const r = this._deps.map(
191
236
  (n) => n.subscribe(() => {
192
237
  const i = this.get();
193
- Object.is(e, i) || (e = i, t(i));
238
+ Object.is(s, i) || (s = i, t(i));
194
239
  })
195
240
  );
196
241
  return () => r.forEach((n) => n());
197
242
  }
198
243
  map(t) {
199
- return y(() => t(this.get()), [this]);
244
+ return C(() => t(this.get()), [this]);
200
245
  }
201
246
  }
202
- function q(s, t) {
203
- return t.map((e) => e !== void 0 && s(e) ? e : void 0);
247
+ function U(e, t) {
248
+ return t.map((s) => s !== void 0 && e(s) ? s : void 0);
204
249
  }
205
- class S {
206
- constructor(t, e) {
207
- this._a = t, this._b = e;
250
+ class D {
251
+ constructor(t, s) {
252
+ this._a = t, this._b = s;
208
253
  }
209
254
  get() {
210
255
  return [this._a.get(), this._b.get()];
211
256
  }
212
- set([t, e]) {
213
- v(() => {
214
- this._a.set(t), this._b.set(e);
257
+ set([t, s]) {
258
+ k(() => {
259
+ this._a.set(t), this._b.set(s);
215
260
  });
216
261
  }
217
262
  subscribe(t) {
218
- const e = () => t(this.get()), r = this._a.subscribe(e), n = this._b.subscribe(e);
263
+ const s = () => t(this.get()), r = this._a.subscribe(s), n = this._b.subscribe(s);
219
264
  return () => {
220
265
  r(), n();
221
266
  };
222
267
  }
223
268
  map(t) {
224
- let e = t(this.get());
225
- const r = m(e);
269
+ let s = t(this.get());
270
+ const r = g(s);
226
271
  return this.subscribe((n) => {
227
272
  const i = t(n);
228
- Object.is(e, i) || (e = i, r.set(i));
273
+ Object.is(s, i) || (s = i, r.set(i));
229
274
  }), r;
230
275
  }
231
276
  focus(t) {
232
- return j(this, t);
277
+ return E(this, t);
233
278
  }
234
279
  narrow(t) {
235
- return k(this, t);
280
+ return P(this, t);
236
281
  }
237
282
  }
238
- function x(s, t) {
239
- return new S(s, t);
283
+ function L(e, t) {
284
+ return new D(e, t);
240
285
  }
241
- function B(s, t) {
242
- return x(m(s), t);
286
+ function X(e, t) {
287
+ return L(g(e), t);
243
288
  }
244
- function h(s, t) {
245
- return { getAll: s, modify: t };
289
+ function v(e, t) {
290
+ return { getAll: e, modify: t };
246
291
  }
247
- function G() {
248
- return h(
249
- (s) => [...s],
250
- (s, t) => s.map(t)
292
+ function Y() {
293
+ return v(
294
+ (e) => [...e],
295
+ (e, t) => e.map(t)
251
296
  );
252
297
  }
253
- function H(s) {
254
- return h(
255
- (t) => t.filter(s),
256
- (t, e) => t.map((r) => s(r) ? e(r) : r)
298
+ function Z(e) {
299
+ return v(
300
+ (t) => t.filter(e),
301
+ (t, s) => t.map((r) => e(r) ? s(r) : r)
257
302
  );
258
303
  }
259
- function I(s) {
260
- return h(
261
- (t) => t.filter((e, r) => r === s),
262
- (t, e) => t.map((r, n) => n === s ? e(r) : r)
304
+ function $(e) {
305
+ return v(
306
+ (t) => t.filter((s, r) => r === e),
307
+ (t, s) => t.map((r, n) => n === e ? s(r) : r)
263
308
  );
264
309
  }
265
- function J(s, t) {
266
- return h(
267
- (e) => t.getAll(s.get(e)),
268
- (e, r) => s.set(e, t.modify(s.get(e), r))
310
+ function tt(e, t) {
311
+ return v(
312
+ (s) => t.getAll(e.view(s)),
313
+ (s, r) => e.review(t.modify(e.view(s), r), s)
269
314
  );
270
315
  }
271
- function K(s, t) {
272
- return h(
273
- (e) => s.getAll(e).flatMap((r) => t.getAll(r)),
274
- (e, r) => s.modify(e, (n) => t.modify(n, r))
316
+ function et(e, t) {
317
+ return v(
318
+ (s) => e.getAll(s).flatMap((r) => t.getAll(r)),
319
+ (s, r) => e.modify(s, (n) => t.modify(n, r))
275
320
  );
276
321
  }
277
- const g = { status: "notAsked" }, b = { status: "loading" }, D = (s) => ({ status: "failure", error: s }), E = (s) => ({ status: "success", value: s }), Q = (s) => s.status === "notAsked", U = (s) => s.status === "loading", V = (s) => s.status === "failure", L = (s) => s.status === "success", X = (s, t) => {
278
- switch (s.status) {
322
+ const d = { status: "notAsked" }, c = { status: "loading" }, m = (e) => ({ status: "failure", error: e }), A = (e) => ({ status: "success", value: e }), st = (e) => e.status === "notAsked", rt = (e) => e.status === "loading", nt = (e) => e.status === "failure", N = (e) => e.status === "success", it = (e, t) => {
323
+ switch (e.status) {
279
324
  case "success":
280
- return E(t(s.value));
325
+ return A(t(e.value));
281
326
  case "failure":
282
- return s;
327
+ return e;
283
328
  case "loading":
284
- return b;
329
+ return c;
285
330
  case "notAsked":
286
- return g;
331
+ return d;
287
332
  }
288
- }, Y = (s, t) => {
289
- switch (s.status) {
333
+ }, ut = (e, t) => {
334
+ switch (e.status) {
290
335
  case "failure":
291
- return D(t(s.error));
336
+ return m(t(e.error));
292
337
  case "success":
293
- return s;
338
+ return e;
294
339
  case "loading":
295
- return b;
340
+ return c;
296
341
  case "notAsked":
297
- return g;
342
+ return d;
298
343
  }
299
- }, Z = (s, t) => {
300
- switch (s.status) {
344
+ }, ot = (e, t) => {
345
+ switch (e.status) {
301
346
  case "success":
302
- return t(s.value);
347
+ return t(e.value);
303
348
  case "failure":
304
- return s;
349
+ return e;
305
350
  case "loading":
306
- return b;
351
+ return c;
307
352
  case "notAsked":
308
- return g;
353
+ return d;
309
354
  }
310
- }, $ = (s, t) => L(s) ? s.value : t, tt = (s, t) => {
311
- switch (s.status) {
355
+ }, ct = (e, t) => N(e) ? e.value : t, at = (e, t) => {
356
+ switch (e.status) {
312
357
  case "notAsked":
313
358
  return t.notAsked();
314
359
  case "loading":
315
360
  return t.loading();
316
361
  case "failure":
317
- return t.failure(s.error);
362
+ return t.failure(e.error);
318
363
  case "success":
319
- return t.success(s.value);
364
+ return t.success(e.value);
320
365
  }
321
366
  };
367
+ function lt(e) {
368
+ const t = g(c);
369
+ return e.then(
370
+ (s) => t.set(A(s)),
371
+ (s) => t.set(m(s))
372
+ ), t;
373
+ }
374
+ function ft(e, t) {
375
+ const s = g(c);
376
+ let r = new AbortController();
377
+ const n = (l) => {
378
+ r.abort(), r = new AbortController(), s.set(c);
379
+ const { signal: o } = r;
380
+ t(l, o).then(
381
+ (O) => {
382
+ o.aborted || s.set(A(O));
383
+ },
384
+ (O) => {
385
+ o.aborted || s.set(m(O));
386
+ }
387
+ );
388
+ };
389
+ n(e.get());
390
+ const i = e.subscribe(n);
391
+ return [s, () => {
392
+ r.abort(), i();
393
+ }];
394
+ }
395
+ function ht(e) {
396
+ const t = g(d);
397
+ let s = null, r = !1;
398
+ return { result: t, trigger: (y) => {
399
+ if (r) return;
400
+ s !== null && s.abort(), s = new AbortController(), t.set(c);
401
+ const { signal: l } = s;
402
+ e(y, l).then(
403
+ (o) => {
404
+ !l.aborted && !r && t.set(A(o));
405
+ },
406
+ (o) => {
407
+ !l.aborted && !r && t.set(m(o));
408
+ }
409
+ );
410
+ }, dispose: () => {
411
+ r = !0, s !== null && (s.abort(), s = null);
412
+ } };
413
+ }
414
+ function bt(e, t) {
415
+ const s = e.get();
416
+ return t === void 0 || t(s) ? Promise.resolve(s) : new Promise((r) => {
417
+ const n = e.subscribe((i) => {
418
+ t(i) && (n(), r(i));
419
+ });
420
+ });
421
+ }
322
422
  export {
323
- v as batch,
324
- Z as chainAsyncData,
325
- P as composeLens,
326
- R as composePrism,
327
- K as composeTraversal,
328
- J as composeWithLens,
329
- y as computed,
330
- q as cond,
331
- G as each,
332
- D as failure,
333
- F as field,
334
- H as filtered,
335
- tt as fold,
336
- z as fst,
337
- $ as getOrElse,
338
- N as id,
339
- V as isFailure,
340
- U as isLoading,
341
- Q as isNotAsked,
342
- L as isSuccess,
343
- W as iso,
344
- _ as lens,
345
- b as loading,
346
- X as mapAsyncData,
347
- Y as mapError,
348
- g as notAsked,
349
- I as nth,
350
- p as prism,
351
- x as product,
352
- m as signal,
353
- C as snd,
354
- T as some,
355
- B as stateful,
356
- E as success,
357
- h as traversal
423
+ V as arrayOf,
424
+ k as batch,
425
+ ot as chainAsyncData,
426
+ F as composeLens,
427
+ B as composePrism,
428
+ et as composeTraversal,
429
+ tt as composeWithLens,
430
+ C as computed,
431
+ U as cond,
432
+ Y as each,
433
+ m as failure,
434
+ R as field,
435
+ Z as filtered,
436
+ at as fold,
437
+ ft as fromAsync,
438
+ ht as fromAsyncImperative,
439
+ lt as fromPromise,
440
+ ct as getOrElse,
441
+ J as guard,
442
+ T as id,
443
+ I as index,
444
+ nt as isFailure,
445
+ rt as isLoading,
446
+ st as isNotAsked,
447
+ N as isSuccess,
448
+ H as iso,
449
+ a as lens,
450
+ c as loading,
451
+ it as mapAsyncData,
452
+ ut as mapError,
453
+ q as mapOf,
454
+ d as notAsked,
455
+ $ as nth,
456
+ K as nullable,
457
+ f as prism,
458
+ L as product,
459
+ W as recordOf,
460
+ g as signal,
461
+ G as some,
462
+ X as stateful,
463
+ A as success,
464
+ Q as tagged,
465
+ bt as toNextValue,
466
+ v as traversal
358
467
  };
@@ -1 +1 @@
1
- (function(n,i){typeof exports=="object"&&typeof module<"u"?i(exports):typeof define=="function"&&define.amd?define(["exports"],i):(n=typeof globalThis<"u"?globalThis:n||self,i(n.Rainbow={}))})(this,(function(n){"use strict";var x=Object.defineProperty;var tt=(n,i,a)=>i in n?x(n,i,{enumerable:!0,configurable:!0,writable:!0,value:a}):n[i]=a;var c=(n,i,a)=>tt(n,typeof i!="symbol"?i+"":i,a);function i(s,t){return{get:s,set:t}}function a(s,t){return{get:e=>t.get(s.get(e)),set:(e,r)=>s.set(e,t.set(s.get(e),r))}}const O=()=>i(([s])=>s,([,s],t)=>[t,s]),L=()=>i(([,s])=>s,([s],t)=>[s,t]);function E(s){return i(t=>t[s],(t,e)=>({...t,[s]:e}))}function T(){return i(s=>s,(s,t)=>t)}function w(s,t){return{match:s,inject:t}}function P(s,t){return{match:e=>{const r=s.match(e);return r!==void 0?t.match(r):void 0},inject:e=>s.inject(t.inject(e))}}function D(){return w(s=>s,s=>s)}function M(s,t){return w(s,t)}let d=0;const _=new Map;function p(s){d++;try{s()}finally{if(d--,d===0)for(;_.size>0;){const t=[..._.values()];_.clear();for(const e of t)e()}}}class F{constructor(t){c(this,"_value");c(this,"_subscribers",new Set);this._value=t}get(){return this._value}set(t){if(!Object.is(this._value,t))if(this._value=t,d>0)for(const e of this._subscribers)_.set(e,()=>e(this._value));else for(const e of this._subscribers)e(t)}subscribe(t){return this._subscribers.add(t),()=>this._subscribers.delete(t)}map(t){return new f(this,t)}focus(t){return new l(this,t)}narrow(t){return new g(this,t)}}class f{constructor(t,e){c(this,"_source");c(this,"_f");this._source=t,this._f=e}get(){return this._f(this._source.get())}subscribe(t){let e=this.get();return this._source.subscribe(()=>{const r=this.get();Object.is(e,r)||(e=r,t(r))})}map(t){return new f(this,t)}}class l{constructor(t,e){c(this,"_source");c(this,"_lens");this._source=t,this._lens=e}get(){return this._lens.get(this._source.get())}set(t){this._source.set(this._lens.set(this._source.get(),t))}subscribe(t){let e=this.get();return this._source.subscribe(()=>{const r=this.get();Object.is(e,r)||(e=r,t(r))})}map(t){return new f(this,t)}focus(t){return new l(this,t)}narrow(t){return new g(this,t)}}class g{constructor(t,e){c(this,"_source");c(this,"_prism");this._source=t,this._prism=e}get(){return this._prism.match(this._source.get())}set(t){t!==void 0&&this._source.set(this._prism.inject(t))}subscribe(t){let e=this.get();return this._source.subscribe(()=>{const r=this.get();Object.is(e,r)||(e=r,t(r))})}map(t){return new f(this,t)}focus(t){return new l(this,t)}narrow(t){return new g(this,t)}}function v(s){return new F(s)}function N(s,t){return new l(s,t)}function R(s,t){return new g(s,t)}function A(s,t){return new W(s,t)}class W{constructor(t,e){c(this,"_fn");c(this,"_deps");this._fn=t,this._deps=e}get(){return this._fn()}subscribe(t){let e=this.get();const r=this._deps.map(u=>u.subscribe(()=>{const o=this.get();Object.is(e,o)||(e=o,t(o))}));return()=>r.forEach(u=>u())}map(t){return A(()=>t(this.get()),[this])}}function z(s,t){return t.map(e=>e!==void 0&&s(e)?e:void 0)}class C{constructor(t,e){this._a=t,this._b=e}get(){return[this._a.get(),this._b.get()]}set([t,e]){p(()=>{this._a.set(t),this._b.set(e)})}subscribe(t){const e=()=>t(this.get()),r=this._a.subscribe(e),u=this._b.subscribe(e);return()=>{r(),u()}}map(t){let e=t(this.get());const r=v(e);return this.subscribe(u=>{const o=t(u);Object.is(e,o)||(e=o,r.set(o))}),r}focus(t){return N(this,t)}narrow(t){return R(this,t)}}function j(s,t){return new C(s,t)}function q(s,t){return j(v(s),t)}function h(s,t){return{getAll:s,modify:t}}function B(){return h(s=>[...s],(s,t)=>s.map(t))}function G(s){return h(t=>t.filter(s),(t,e)=>t.map(r=>s(r)?e(r):r))}function H(s){return h(t=>t.filter((e,r)=>r===s),(t,e)=>t.map((r,u)=>u===s?e(r):r))}function I(s,t){return h(e=>t.getAll(s.get(e)),(e,r)=>s.set(e,t.modify(s.get(e),r)))}function J(s,t){return h(e=>s.getAll(e).flatMap(r=>t.getAll(r)),(e,r)=>s.modify(e,u=>t.modify(u,r)))}const b={status:"notAsked"},m={status:"loading"},k=s=>({status:"failure",error:s}),y=s=>({status:"success",value:s}),K=s=>s.status==="notAsked",Q=s=>s.status==="loading",U=s=>s.status==="failure",S=s=>s.status==="success",V=(s,t)=>{switch(s.status){case"success":return y(t(s.value));case"failure":return s;case"loading":return m;case"notAsked":return b}},X=(s,t)=>{switch(s.status){case"failure":return k(t(s.error));case"success":return s;case"loading":return m;case"notAsked":return b}},Y=(s,t)=>{switch(s.status){case"success":return t(s.value);case"failure":return s;case"loading":return m;case"notAsked":return b}},Z=(s,t)=>S(s)?s.value:t,$=(s,t)=>{switch(s.status){case"notAsked":return t.notAsked();case"loading":return t.loading();case"failure":return t.failure(s.error);case"success":return t.success(s.value)}};n.batch=p,n.chainAsyncData=Y,n.composeLens=a,n.composePrism=P,n.composeTraversal=J,n.composeWithLens=I,n.computed=A,n.cond=z,n.each=B,n.failure=k,n.field=E,n.filtered=G,n.fold=$,n.fst=O,n.getOrElse=Z,n.id=T,n.isFailure=U,n.isLoading=Q,n.isNotAsked=K,n.isSuccess=S,n.iso=M,n.lens=i,n.loading=m,n.mapAsyncData=V,n.mapError=X,n.notAsked=b,n.nth=H,n.prism=w,n.product=j,n.signal=v,n.snd=L,n.some=D,n.stateful=q,n.success=y,n.traversal=h,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(r,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(r=typeof globalThis<"u"?globalThis:r||self,u(r.Rainbow={}))})(this,(function(r){"use strict";var he=Object.defineProperty;var de=(r,u,f)=>u in r?he(r,u,{enumerable:!0,configurable:!0,writable:!0,value:f}):r[u]=f;var o=(r,u,f)=>de(r,typeof u!="symbol"?u+"":u,f);function u(t,e){return{view:t,review:e}}function f(t,e){return{view:s=>e.view(t.view(s)),review:(s,n)=>t.review(e.review(s,t.view(n)),n)}}function M(t){return u(e=>e[t],(e,s)=>{const n=[...s];return n[t]=e,n})}function T(t){return u(e=>e[t],(e,s)=>({...s,[t]:e}))}function C(){return u(t=>t,t=>t)}function N(t){return u(e=>e.map(s=>t.view(s)),(e,s)=>s.map((n,i)=>t.review(e[i],n)))}function D(t){return u(e=>Object.fromEntries(Object.entries(e).map(([s,n])=>[s,t.view(n)])),(e,s)=>Object.fromEntries(Object.entries(s).map(([n,i])=>[n,t.review(e[n],i)])))}function F(t){return u(e=>new Map([...e].map(([s,n])=>[s,t.view(n)])),(e,s)=>new Map([...s].map(([n,i])=>[n,t.review(e.get(n),i)])))}function h(t,e){return{view:t,review:e}}function I(t,e){return{view:s=>{const n=t.view(s);return n!==void 0?e.view(n):void 0},review:s=>t.review(e.review(s))}}function R(){return h(t=>t,t=>t)}function V(t,e){return h(t,e)}function W(t){return h(e=>t(e)?e:void 0,e=>e)}function z(){return h(t=>t!==null?t:void 0,t=>t)}function q(t,e){return h(s=>s[t]===e?s:void 0,s=>s)}let O=0;const y=new Map;function S(t){O++;try{t()}finally{if(O--,O===0)for(;y.size>0;){const e=[...y.values()];y.clear();for(const s of e)s()}}}class B{constructor(e){o(this,"_value");o(this,"_subscribers",new Set);this._value=e}get(){return this._value}set(e){if(!Object.is(this._value,e))if(this._value=e,O>0)for(const s of this._subscribers)y.set(s,()=>s(this._value));else for(const s of this._subscribers)s(e)}subscribe(e){return this._subscribers.add(e),()=>this._subscribers.delete(e)}map(e){return new g(this,e)}focus(e){return new m(this,e)}narrow(e){return new w(this,e)}}class g{constructor(e,s){o(this,"_source");o(this,"_f");this._source=e,this._f=s}get(){return this._f(this._source.get())}subscribe(e){let s=this.get();return this._source.subscribe(()=>{const n=this.get();Object.is(s,n)||(s=n,e(n))})}map(e){return new g(this,e)}}class m{constructor(e,s){o(this,"_source");o(this,"_lens");this._source=e,this._lens=s}get(){return this._lens.view(this._source.get())}set(e){this._source.set(this._lens.review(e,this._source.get()))}subscribe(e){let s=this.get();return this._source.subscribe(()=>{const n=this.get();Object.is(s,n)||(s=n,e(n))})}map(e){return new g(this,e)}focus(e){return new m(this,e)}narrow(e){return new w(this,e)}}class w{constructor(e,s){o(this,"_source");o(this,"_prism");this._source=e,this._prism=s}get(){return this._prism.view(this._source.get())}set(e){e!==void 0&&this._source.set(this._prism.review(e))}subscribe(e){let s=this.get();return this._source.subscribe(()=>{const n=this.get();Object.is(s,n)||(s=n,e(n))})}map(e){return new g(this,e)}focus(e){return new m(this,e)}narrow(e){return new w(this,e)}}function d(t){return new B(t)}function G(t,e){return new m(t,e)}function H(t,e){return new w(t,e)}function k(t,e){return new J(t,e)}class J{constructor(e,s){o(this,"_fn");o(this,"_deps");this._fn=e,this._deps=s}get(){return this._fn()}subscribe(e){let s=this.get();const n=this._deps.map(i=>i.subscribe(()=>{const c=this.get();Object.is(s,c)||(s=c,e(c))}));return()=>n.forEach(i=>i())}map(e){return k(()=>e(this.get()),[this])}}function K(t,e){return e.map(s=>s!==void 0&&t(s)?s:void 0)}class Q{constructor(e,s){this._a=e,this._b=s}get(){return[this._a.get(),this._b.get()]}set([e,s]){S(()=>{this._a.set(e),this._b.set(s)})}subscribe(e){const s=()=>e(this.get()),n=this._a.subscribe(s),i=this._b.subscribe(s);return()=>{n(),i()}}map(e){let s=e(this.get());const n=d(s);return this.subscribe(i=>{const c=e(i);Object.is(s,c)||(s=c,n.set(c))}),n}focus(e){return G(this,e)}narrow(e){return H(this,e)}}function P(t,e){return new Q(t,e)}function U(t,e){return P(d(t),e)}function b(t,e){return{getAll:t,modify:e}}function X(){return b(t=>[...t],(t,e)=>t.map(e))}function Y(t){return b(e=>e.filter(t),(e,s)=>e.map(n=>t(n)?s(n):n))}function Z(t){return b(e=>e.filter((s,n)=>n===t),(e,s)=>e.map((n,i)=>i===t?s(n):n))}function $(t,e){return b(s=>e.getAll(t.view(s)),(s,n)=>t.review(e.modify(t.view(s),n),s))}function x(t,e){return b(s=>t.getAll(s).flatMap(n=>e.getAll(n)),(s,n)=>t.modify(s,i=>e.modify(i,n)))}const v={status:"notAsked"},a={status:"loading"},_=t=>({status:"failure",error:t}),p=t=>({status:"success",value:t}),ee=t=>t.status==="notAsked",te=t=>t.status==="loading",se=t=>t.status==="failure",E=t=>t.status==="success",ne=(t,e)=>{switch(t.status){case"success":return p(e(t.value));case"failure":return t;case"loading":return a;case"notAsked":return v}},re=(t,e)=>{switch(t.status){case"failure":return _(e(t.error));case"success":return t;case"loading":return a;case"notAsked":return v}},ie=(t,e)=>{switch(t.status){case"success":return e(t.value);case"failure":return t;case"loading":return a;case"notAsked":return v}},ue=(t,e)=>E(t)?t.value:e,ce=(t,e)=>{switch(t.status){case"notAsked":return e.notAsked();case"loading":return e.loading();case"failure":return e.failure(t.error);case"success":return e.success(t.value)}};function oe(t){const e=d(a);return t.then(s=>e.set(p(s)),s=>e.set(_(s))),e}function ae(t,e){const s=d(a);let n=new AbortController;const i=A=>{n.abort(),n=new AbortController,s.set(a);const{signal:l}=n;e(A,l).then(j=>{l.aborted||s.set(p(j))},j=>{l.aborted||s.set(_(j))})};i(t.get());const c=t.subscribe(i);return[s,()=>{n.abort(),c()}]}function le(t){const e=d(v);let s=null,n=!1;return{result:e,trigger:L=>{if(n)return;s!==null&&s.abort(),s=new AbortController,e.set(a);const{signal:A}=s;t(L,A).then(l=>{!A.aborted&&!n&&e.set(p(l))},l=>{!A.aborted&&!n&&e.set(_(l))})},dispose:()=>{n=!0,s!==null&&(s.abort(),s=null)}}}function fe(t,e){const s=t.get();return e===void 0||e(s)?Promise.resolve(s):new Promise(n=>{const i=t.subscribe(c=>{e(c)&&(i(),n(c))})})}r.arrayOf=N,r.batch=S,r.chainAsyncData=ie,r.composeLens=f,r.composePrism=I,r.composeTraversal=x,r.composeWithLens=$,r.computed=k,r.cond=K,r.each=X,r.failure=_,r.field=T,r.filtered=Y,r.fold=ce,r.fromAsync=ae,r.fromAsyncImperative=le,r.fromPromise=oe,r.getOrElse=ue,r.guard=W,r.id=C,r.index=M,r.isFailure=se,r.isLoading=te,r.isNotAsked=ee,r.isSuccess=E,r.iso=V,r.lens=u,r.loading=a,r.mapAsyncData=ne,r.mapError=re,r.mapOf=F,r.notAsked=v,r.nth=Z,r.nullable=z,r.prism=h,r.product=P,r.recordOf=D,r.signal=d,r.some=R,r.stateful=U,r.success=p,r.tagged=q,r.toNextValue=fe,r.traversal=b,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})}));
package/dist/signal.d.ts CHANGED
@@ -13,22 +13,47 @@ export declare function batch(fn: () => void): void;
13
13
  * Reading tracks the dependency; writing propagates to subscribers.
14
14
  */
15
15
  export interface Signal<A> {
16
+ /** Return the current value. */
16
17
  get(): A;
18
+ /** Update the value and notify subscribers. No-op if the value is unchanged (`Object.is`). */
17
19
  set(a: A): void;
20
+ /**
21
+ * Subscribe to value changes.
22
+ * @returns An unsubscribe function.
23
+ */
18
24
  subscribe(fn: Subscriber<A>): () => void;
19
- /** Read-only derived signal */
25
+ /** Return a read-only derived signal by applying `f` to each value. */
20
26
  map<B>(f: (a: A) => B): ReadonlySignal<B>;
21
- /** Read-write focused signal via lens */
27
+ /** Return a read-write signal focused on B via a lens. */
22
28
  focus<B>(lens: Lens<A, B>): Signal<B>;
23
- /** Read-write focused signal via prism (undefined when case doesn't match) */
29
+ /** Return a read-write signal focused via a prism; yields `undefined` when the case doesn't match. */
24
30
  narrow<B>(prism: Prism<A, B>): Signal<B | undefined>;
25
31
  }
32
+ /** A read-only view of a reactive value. */
26
33
  export interface ReadonlySignal<A> {
34
+ /** Return the current value. */
27
35
  get(): A;
36
+ /**
37
+ * Subscribe to value changes.
38
+ * @returns An unsubscribe function.
39
+ */
28
40
  subscribe(fn: Subscriber<A>): () => void;
41
+ /** Return a read-only derived signal by applying `f` to each value. */
29
42
  map<B>(f: (a: A) => B): ReadonlySignal<B>;
30
43
  }
44
+ /**
45
+ * Create a root signal with the given initial value.
46
+ * @param initial - The starting value.
47
+ */
31
48
  export declare function signal<A>(initial: A): Signal<A>;
49
+ /**
50
+ * Create a signal focused on part of another signal via a lens.
51
+ * Reads and writes pass through the lens; the source signal is the source of truth.
52
+ */
32
53
  export declare function focusSignal<A, B>(source: Signal<A>, lens: Lens<A, B>): Signal<B>;
54
+ /**
55
+ * Create a signal focused on a prism case of another signal.
56
+ * Yields `undefined` when the prism doesn't match; writes are no-ops when `b` is `undefined`.
57
+ */
33
58
  export declare function narrowSignal<A, B>(source: Signal<A>, prism: Prism<A, B>): Signal<B | undefined>;
34
59
  export {};
@@ -7,17 +7,36 @@ import type { Lens } from './lens.ts';
7
7
  * modify(modify(a, f), g) = modify(a, g ∘ f) [when f, g commute on elements]
8
8
  */
9
9
  export interface Traversal<A, B> {
10
+ /** Extract all focused values from `a`. */
10
11
  getAll(a: A): B[];
12
+ /** Apply `f` to every focused value and return the updated `a`. */
11
13
  modify(a: A, f: (b: B) => B): A;
12
14
  }
15
+ /**
16
+ * Construct a traversal from explicit getAll and modify functions.
17
+ * @param getAll - Extract all focused values.
18
+ * @param modify - Apply a function to every focused value.
19
+ */
13
20
  export declare function traversal<A, B>(getAll: (a: A) => B[], modify: (a: A, f: (b: B) => B) => A): Traversal<A, B>;
14
21
  /** Focus on every element of an array */
15
22
  export declare function each<B>(): Traversal<B[], B>;
16
- /** Focus on elements matching a predicate */
23
+ /**
24
+ * Focus on elements matching a predicate.
25
+ * Non-matching elements pass through unmodified.
26
+ */
17
27
  export declare function filtered<B>(pred: (b: B) => boolean): Traversal<B[], B>;
18
- /** Focus on a single element by index */
28
+ /**
29
+ * Focus on a single element by index.
30
+ * If the index is out of bounds, `getAll` returns `[]` and `modify` is a no-op.
31
+ */
19
32
  export declare function nth<B>(index: number): Traversal<B[], B>;
20
- /** Compose a lens with a traversal */
33
+ /**
34
+ * Compose a lens with a traversal.
35
+ * Focuses the lens on B within A, then the traversal on C within B.
36
+ */
21
37
  export declare function composeWithLens<A, B extends object, C>(lens: Lens<A, B>, t: Traversal<B, C>): Traversal<A, C>;
22
- /** Compose two traversals */
38
+ /**
39
+ * Compose two traversals.
40
+ * `getAll` flatMaps; `modify` nests.
41
+ */
23
42
  export declare function composeTraversal<A, B, C>(ab: Traversal<A, B>, bc: Traversal<B, C>): Traversal<A, C>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhi-zone/rainbow",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-alpha.0",
4
4
  "description": "Optics-based reactivity — structured state for the web",
5
5
  "type": "module",
6
6
  "main": "./dist/rainbow.umd.cjs",
@@ -15,18 +15,18 @@
15
15
  },
16
16
  "files": ["dist"],
17
17
  "scripts": {
18
- "dev": "vite build --watch",
19
- "build": "vite build && tsgo --emitDeclarationOnly",
20
- "typecheck": "tsgo --noEmit",
21
- "test": "vitest run",
18
+ "dev": "vite build --watch",
19
+ "build": "vite build && tsgo --emitDeclarationOnly",
20
+ "typecheck": "tsgo --noEmit",
21
+ "test": "vitest run",
22
22
  "test:watch": "vitest"
23
23
  },
24
24
  "devDependencies": {
25
- "@types/react": "^19.2.14",
25
+ "@types/react": "^19.2.14",
26
26
  "@vitejs/plugin-vue": "^6.0.4",
27
- "fast-check": "^4.5.3",
28
- "vite": "^6.0.0",
29
- "vitest": "^2.0.0",
30
- "vue": "^3.5.29"
27
+ "fast-check": "^4.5.3",
28
+ "vite": "^6.0.0",
29
+ "vitest": "^2.0.0",
30
+ "vue": "^3.5.29"
31
31
  }
32
32
  }