@rhi-zone/rainbow 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.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * AsyncData<T, E> — the type of an asynchronous value.
3
+ *
4
+ * Four states:
5
+ * notAsked — not yet requested
6
+ * loading — request in flight
7
+ * failure — completed with error
8
+ * success — completed with value
9
+ *
10
+ * Modelled as a discriminated union so exhaustive pattern matching is
11
+ * enforced by the type system.
12
+ */
13
+ export type AsyncData<T, E = unknown> = {
14
+ readonly status: 'notAsked';
15
+ } | {
16
+ readonly status: 'loading';
17
+ } | {
18
+ readonly status: 'failure';
19
+ readonly error: E;
20
+ } | {
21
+ readonly status: 'success';
22
+ readonly value: T;
23
+ };
24
+ export declare const notAsked: AsyncData<never, never>;
25
+ export declare const loading: AsyncData<never, never>;
26
+ export declare const failure: <E>(error: E) => AsyncData<never, E>;
27
+ export declare const success: <T>(value: T) => AsyncData<T, never>;
28
+ export declare const isNotAsked: <T, E>(ad: AsyncData<T, E>) => ad is {
29
+ status: 'notAsked';
30
+ };
31
+ export declare const isLoading: <T, E>(ad: AsyncData<T, E>) => ad is {
32
+ status: 'loading';
33
+ };
34
+ export declare const isFailure: <T, E>(ad: AsyncData<T, E>) => ad is {
35
+ status: 'failure';
36
+ error: E;
37
+ };
38
+ export declare const isSuccess: <T, E>(ad: AsyncData<T, E>) => ad is {
39
+ status: 'success';
40
+ value: T;
41
+ };
42
+ /** Transform the success value, leaving other states untouched. */
43
+ export declare const map: <T, U, E>(ad: AsyncData<T, E>, f: (value: T) => U) => AsyncData<U, E>;
44
+ /** Transform the error value, leaving other states untouched. */
45
+ export declare const mapError: <T, E, F>(ad: AsyncData<T, E>, f: (error: E) => F) => AsyncData<T, F>;
46
+ /** Chain async operations — flatMap over the success case. */
47
+ 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. */
49
+ export declare const getOrElse: <T, E>(ad: AsyncData<T, E>, fallback: T) => T;
50
+ /** Fold over all four states. */
51
+ export declare const fold: <T, E, R>(ad: AsyncData<T, E>, cases: {
52
+ notAsked: () => R;
53
+ loading: () => R;
54
+ failure: (error: E) => R;
55
+ success: (value: T) => R;
56
+ }) => R;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { ReadonlySignal } from './signal.ts';
2
+ /**
3
+ * Derive a read-only signal from multiple source signals.
4
+ *
5
+ * Unlike signal.map (one source), computed accepts any number of sources
6
+ * and recomputes when any of them change.
7
+ */
8
+ export declare function computed<T>(fn: () => T, deps: ReadonlySignal<unknown>[]): ReadonlySignal<T>;
@@ -0,0 +1 @@
1
+ export {};
package/dist/cond.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { ReadonlySignal } from './signal.ts';
2
+ /**
3
+ * Conditional signal — propagates the value when `pred` holds, undefined otherwise.
4
+ *
5
+ * Accepts both `ReadonlySignal<A>` and `ReadonlySignal<A | undefined>` so that
6
+ * cond calls can be composed directly:
7
+ *
8
+ * cond(p, cond(q, w)) ≡ cond(x => q(x) && p(x), w)
9
+ *
10
+ * When the source already carries `undefined` (from a prior `cond` or `narrow`),
11
+ * undefined passes through unchanged — equivalent to short-circuit `&&`.
12
+ *
13
+ * Usage:
14
+ * const positiveCount = cond(n => n > 0, countSignal)
15
+ * const positiveEven = cond(n => n % 2 === 0, positiveCount)
16
+ */
17
+ export declare function cond<A>(pred: (a: A) => boolean, s: ReadonlySignal<A>): ReadonlySignal<A | undefined>;
18
+ export declare function cond<A>(pred: (a: A) => boolean, s: ReadonlySignal<A | undefined>): ReadonlySignal<A | undefined>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ export type { Lens } from './lens.ts';
2
+ export { lens, composeLens, field, fst, snd, id } from './lens.ts';
3
+ export type { Prism } from './prism.ts';
4
+ export { prism, composePrism, some, iso } from './prism.ts';
5
+ export type { Signal, ReadonlySignal } from './signal.ts';
6
+ export { signal, batch } from './signal.ts';
7
+ export { computed } from './computed.ts';
8
+ export { cond } from './cond.ts';
9
+ export { product, stateful } from './product.ts';
10
+ export type { Traversal } from './traversal.ts';
11
+ export { traversal, each, filtered, nth, composeWithLens, composeTraversal } from './traversal.ts';
12
+ 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';
@@ -0,0 +1 @@
1
+ export {};
package/dist/lens.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A Lens<A, B> focuses on a field of type B within a structure of type A.
3
+ *
4
+ * Laws:
5
+ * get(set(a, b)) = b
6
+ * set(a, get(a)) = a
7
+ * set(set(a, b1), b2) = set(a, b2)
8
+ */
9
+ export interface Lens<A, B> {
10
+ get(a: A): B;
11
+ set(a: A, b: B): A;
12
+ }
13
+ export declare function lens<A, B>(get: (a: A) => B, set: (a: A, b: B) => A): Lens<A, B>;
14
+ 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 */
20
+ export declare function field<A, K extends keyof A>(key: K): Lens<A, A[K]>;
21
+ /** Identity lens */
22
+ export declare function id<A>(): Lens<A, A>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A Prism<A, B> focuses on a case of type B within a sum type A.
3
+ *
4
+ * Laws:
5
+ * match(inject(b)) = b
6
+ * if match(a) = b then inject(b) = a
7
+ */
8
+ export interface Prism<A, B> {
9
+ match(a: A): B | undefined;
10
+ inject(b: B): A;
11
+ }
12
+ export declare function prism<A, B>(match: (a: A) => B | undefined, inject: (b: B) => A): Prism<A, B>;
13
+ export declare function composePrism<A, B, C>(ab: Prism<A, B>, bc: Prism<B, C>): Prism<A, C>;
14
+ /** Prism for the Some case of an optional value */
15
+ export declare function some<A>(): Prism<A | undefined, A>;
16
+ /** Prism that always matches (isomorphism) */
17
+ export declare function iso<A, B>(to: (a: A) => B, from: (b: B) => A): Prism<A, B>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { Signal } from './signal.ts';
2
+ /**
3
+ * Create a signal over a pair [A, B] backed by two independent signals.
4
+ */
5
+ export declare function product<A, B>(a: Signal<A>, b: Signal<B>): Signal<[A, B]>;
6
+ /**
7
+ * Encapsulate internal state S alongside external signal A.
8
+ *
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()).
11
+ *
12
+ * Unicorn equivalent: `stateful init widget`
13
+ *
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
18
+ */
19
+ export declare function stateful<S, A>(init: S, outer: Signal<A>): Signal<[S, A]>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,358 @@
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 };
6
+ }
7
+ function P(s, t) {
8
+ return {
9
+ get: (e) => t.get(s.get(e)),
10
+ set: (e, r) => s.set(e, t.set(s.get(e), r))
11
+ };
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 })
18
+ );
19
+ }
20
+ function N() {
21
+ return _((s) => s, (s, t) => t);
22
+ }
23
+ function p(s, t) {
24
+ return { match: s, inject: t };
25
+ }
26
+ function R(s, t) {
27
+ return {
28
+ match: (e) => {
29
+ const r = s.match(e);
30
+ return r !== void 0 ? t.match(r) : void 0;
31
+ },
32
+ inject: (e) => s.inject(t.inject(e))
33
+ };
34
+ }
35
+ function T() {
36
+ return p(
37
+ (s) => s,
38
+ (s) => s
39
+ );
40
+ }
41
+ function W(s, t) {
42
+ return p(s, t);
43
+ }
44
+ let l = 0;
45
+ const f = /* @__PURE__ */ new Map();
46
+ function v(s) {
47
+ l++;
48
+ try {
49
+ s();
50
+ } 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();
56
+ }
57
+ }
58
+ }
59
+ class A {
60
+ constructor(t) {
61
+ u(this, "_value");
62
+ u(this, "_subscribers", /* @__PURE__ */ new Set());
63
+ this._value = t;
64
+ }
65
+ get() {
66
+ return this._value;
67
+ }
68
+ set(t) {
69
+ 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));
73
+ else
74
+ for (const e of this._subscribers) e(t);
75
+ }
76
+ subscribe(t) {
77
+ return this._subscribers.add(t), () => this._subscribers.delete(t);
78
+ }
79
+ map(t) {
80
+ return new c(this, t);
81
+ }
82
+ focus(t) {
83
+ return new o(this, t);
84
+ }
85
+ narrow(t) {
86
+ return new a(this, t);
87
+ }
88
+ }
89
+ class c {
90
+ constructor(t, e) {
91
+ u(this, "_source");
92
+ u(this, "_f");
93
+ this._source = t, this._f = e;
94
+ }
95
+ get() {
96
+ return this._f(this._source.get());
97
+ }
98
+ subscribe(t) {
99
+ let e = this.get();
100
+ return this._source.subscribe(() => {
101
+ const r = this.get();
102
+ Object.is(e, r) || (e = r, t(r));
103
+ });
104
+ }
105
+ map(t) {
106
+ return new c(this, t);
107
+ }
108
+ }
109
+ class o {
110
+ constructor(t, e) {
111
+ u(this, "_source");
112
+ u(this, "_lens");
113
+ this._source = t, this._lens = e;
114
+ }
115
+ get() {
116
+ return this._lens.get(this._source.get());
117
+ }
118
+ set(t) {
119
+ this._source.set(this._lens.set(this._source.get(), t));
120
+ }
121
+ subscribe(t) {
122
+ let e = this.get();
123
+ return this._source.subscribe(() => {
124
+ const r = this.get();
125
+ Object.is(e, r) || (e = r, t(r));
126
+ });
127
+ }
128
+ map(t) {
129
+ return new c(this, t);
130
+ }
131
+ focus(t) {
132
+ return new o(this, t);
133
+ }
134
+ narrow(t) {
135
+ return new a(this, t);
136
+ }
137
+ }
138
+ class a {
139
+ constructor(t, e) {
140
+ u(this, "_source");
141
+ u(this, "_prism");
142
+ this._source = t, this._prism = e;
143
+ }
144
+ get() {
145
+ return this._prism.match(this._source.get());
146
+ }
147
+ set(t) {
148
+ t !== void 0 && this._source.set(this._prism.inject(t));
149
+ }
150
+ subscribe(t) {
151
+ let e = this.get();
152
+ return this._source.subscribe(() => {
153
+ const r = this.get();
154
+ Object.is(e, r) || (e = r, t(r));
155
+ });
156
+ }
157
+ map(t) {
158
+ return new c(this, t);
159
+ }
160
+ focus(t) {
161
+ return new o(this, t);
162
+ }
163
+ narrow(t) {
164
+ return new a(this, t);
165
+ }
166
+ }
167
+ function m(s) {
168
+ return new A(s);
169
+ }
170
+ function j(s, t) {
171
+ return new o(s, t);
172
+ }
173
+ function k(s, t) {
174
+ return new a(s, t);
175
+ }
176
+ function y(s, t) {
177
+ return new O(s, t);
178
+ }
179
+ class O {
180
+ constructor(t, e) {
181
+ u(this, "_fn");
182
+ u(this, "_deps");
183
+ this._fn = t, this._deps = e;
184
+ }
185
+ get() {
186
+ return this._fn();
187
+ }
188
+ subscribe(t) {
189
+ let e = this.get();
190
+ const r = this._deps.map(
191
+ (n) => n.subscribe(() => {
192
+ const i = this.get();
193
+ Object.is(e, i) || (e = i, t(i));
194
+ })
195
+ );
196
+ return () => r.forEach((n) => n());
197
+ }
198
+ map(t) {
199
+ return y(() => t(this.get()), [this]);
200
+ }
201
+ }
202
+ function q(s, t) {
203
+ return t.map((e) => e !== void 0 && s(e) ? e : void 0);
204
+ }
205
+ class S {
206
+ constructor(t, e) {
207
+ this._a = t, this._b = e;
208
+ }
209
+ get() {
210
+ return [this._a.get(), this._b.get()];
211
+ }
212
+ set([t, e]) {
213
+ v(() => {
214
+ this._a.set(t), this._b.set(e);
215
+ });
216
+ }
217
+ subscribe(t) {
218
+ const e = () => t(this.get()), r = this._a.subscribe(e), n = this._b.subscribe(e);
219
+ return () => {
220
+ r(), n();
221
+ };
222
+ }
223
+ map(t) {
224
+ let e = t(this.get());
225
+ const r = m(e);
226
+ return this.subscribe((n) => {
227
+ const i = t(n);
228
+ Object.is(e, i) || (e = i, r.set(i));
229
+ }), r;
230
+ }
231
+ focus(t) {
232
+ return j(this, t);
233
+ }
234
+ narrow(t) {
235
+ return k(this, t);
236
+ }
237
+ }
238
+ function x(s, t) {
239
+ return new S(s, t);
240
+ }
241
+ function B(s, t) {
242
+ return x(m(s), t);
243
+ }
244
+ function h(s, t) {
245
+ return { getAll: s, modify: t };
246
+ }
247
+ function G() {
248
+ return h(
249
+ (s) => [...s],
250
+ (s, t) => s.map(t)
251
+ );
252
+ }
253
+ function H(s) {
254
+ return h(
255
+ (t) => t.filter(s),
256
+ (t, e) => t.map((r) => s(r) ? e(r) : r)
257
+ );
258
+ }
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)
263
+ );
264
+ }
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))
269
+ );
270
+ }
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))
275
+ );
276
+ }
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) {
279
+ case "success":
280
+ return E(t(s.value));
281
+ case "failure":
282
+ return s;
283
+ case "loading":
284
+ return b;
285
+ case "notAsked":
286
+ return g;
287
+ }
288
+ }, Y = (s, t) => {
289
+ switch (s.status) {
290
+ case "failure":
291
+ return D(t(s.error));
292
+ case "success":
293
+ return s;
294
+ case "loading":
295
+ return b;
296
+ case "notAsked":
297
+ return g;
298
+ }
299
+ }, Z = (s, t) => {
300
+ switch (s.status) {
301
+ case "success":
302
+ return t(s.value);
303
+ case "failure":
304
+ return s;
305
+ case "loading":
306
+ return b;
307
+ case "notAsked":
308
+ return g;
309
+ }
310
+ }, $ = (s, t) => L(s) ? s.value : t, tt = (s, t) => {
311
+ switch (s.status) {
312
+ case "notAsked":
313
+ return t.notAsked();
314
+ case "loading":
315
+ return t.loading();
316
+ case "failure":
317
+ return t.failure(s.error);
318
+ case "success":
319
+ return t.success(s.value);
320
+ }
321
+ };
322
+ 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
358
+ };
@@ -0,0 +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"})}));
@@ -0,0 +1,21 @@
1
+ import type { Signal, ReadonlySignal } from './signal.ts';
2
+ /**
3
+ * Subscribe to a read-only rainbow signal in a React component.
4
+ *
5
+ * Uses useSyncExternalStore for concurrent-mode safety.
6
+ * The component re-renders whenever the signal value changes.
7
+ *
8
+ * Usage:
9
+ * const count = useReadonlySignal(activeCount)
10
+ */
11
+ export declare function useReadonlySignal<A>(s: ReadonlySignal<A>): A;
12
+ /**
13
+ * Subscribe to a read-write rainbow signal in a React component.
14
+ *
15
+ * Returns [currentValue, setter] — analogous to useState, but backed
16
+ * by a shared signal rather than local component state.
17
+ *
18
+ * Usage:
19
+ * const [draft, setDraft] = useSignal(draftSignal)
20
+ */
21
+ export declare function useSignal<A>(s: Signal<A>): [A, (a: A) => void];
@@ -0,0 +1,34 @@
1
+ import type { Lens } from './lens.ts';
2
+ import type { Prism } from './prism.ts';
3
+ type Subscriber<A> = (value: A) => void;
4
+ /**
5
+ * Run `fn` synchronously; defer and deduplicate all signal notifications
6
+ * until `fn` returns, then flush them in one pass.
7
+ *
8
+ * Batches may nest — flush only happens when the outermost batch completes.
9
+ */
10
+ export declare function batch(fn: () => void): void;
11
+ /**
12
+ * A Signal<A> is a reactive cell holding a value of type A.
13
+ * Reading tracks the dependency; writing propagates to subscribers.
14
+ */
15
+ export interface Signal<A> {
16
+ get(): A;
17
+ set(a: A): void;
18
+ subscribe(fn: Subscriber<A>): () => void;
19
+ /** Read-only derived signal */
20
+ map<B>(f: (a: A) => B): ReadonlySignal<B>;
21
+ /** Read-write focused signal via lens */
22
+ focus<B>(lens: Lens<A, B>): Signal<B>;
23
+ /** Read-write focused signal via prism (undefined when case doesn't match) */
24
+ narrow<B>(prism: Prism<A, B>): Signal<B | undefined>;
25
+ }
26
+ export interface ReadonlySignal<A> {
27
+ get(): A;
28
+ subscribe(fn: Subscriber<A>): () => void;
29
+ map<B>(f: (a: A) => B): ReadonlySignal<B>;
30
+ }
31
+ export declare function signal<A>(initial: A): Signal<A>;
32
+ export declare function focusSignal<A, B>(source: Signal<A>, lens: Lens<A, B>): Signal<B>;
33
+ export declare function narrowSignal<A, B>(source: Signal<A>, prism: Prism<A, B>): Signal<B | undefined>;
34
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { Lens } from './lens.ts';
2
+ /**
3
+ * A Traversal<A, B> focuses on zero or more B values within A.
4
+ *
5
+ * Laws:
6
+ * modify(a, id) = a
7
+ * modify(modify(a, f), g) = modify(a, g ∘ f) [when f, g commute on elements]
8
+ */
9
+ export interface Traversal<A, B> {
10
+ getAll(a: A): B[];
11
+ modify(a: A, f: (b: B) => B): A;
12
+ }
13
+ export declare function traversal<A, B>(getAll: (a: A) => B[], modify: (a: A, f: (b: B) => B) => A): Traversal<A, B>;
14
+ /** Focus on every element of an array */
15
+ export declare function each<B>(): Traversal<B[], B>;
16
+ /** Focus on elements matching a predicate */
17
+ export declare function filtered<B>(pred: (b: B) => boolean): Traversal<B[], B>;
18
+ /** Focus on a single element by index */
19
+ export declare function nth<B>(index: number): Traversal<B[], B>;
20
+ /** Compose a lens with a traversal */
21
+ export declare function composeWithLens<A, B extends object, C>(lens: Lens<A, B>, t: Traversal<B, C>): Traversal<A, C>;
22
+ /** Compose two traversals */
23
+ export declare function composeTraversal<A, B, C>(ab: Traversal<A, B>, bc: Traversal<B, C>): Traversal<A, C>;
@@ -0,0 +1 @@
1
+ export {};
package/dist/vue.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { type Ref } from 'vue';
2
+ import type { Signal, ReadonlySignal } from './signal.ts';
3
+ /**
4
+ * Adapt a rainbow Signal<A> to a Vue Ref<A>.
5
+ * Changes to the signal propagate to Vue's reactivity system, and vice versa.
6
+ */
7
+ export declare function signalToRef<A>(s: Signal<A>): Ref<A>;
8
+ /**
9
+ * Adapt a rainbow ReadonlySignal<A> to a read-only Vue Ref<A>.
10
+ */
11
+ export declare function readonlySignalToRef<A>(s: ReadonlySignal<A>): Ref<A>;
12
+ /**
13
+ * Convert a map of signals to a map of Vue Refs in one call.
14
+ *
15
+ * Distinguishes Signal (read-write) from ReadonlySignal (read-only)
16
+ * via duck-typing on the `set` method.
17
+ *
18
+ * Usage:
19
+ * const { draft, filteredTodos, activeCount } = useSignals({ draft, filteredTodos, activeCount })
20
+ */
21
+ export declare function useSignals<T extends Record<string, Signal<unknown> | ReadonlySignal<unknown>>>(signals: T): {
22
+ [K in keyof T]: Ref<T[K] extends Signal<infer A> | ReadonlySignal<infer A> ? A : never>;
23
+ };
24
+ /**
25
+ * Adapt a Vue Ref<A> to a rainbow Signal<A>.
26
+ *
27
+ * Creates a rainbow signal backed by the ref's current value,
28
+ * then keeps them in sync bidirectionally.
29
+ */
30
+ export declare function refToSignal<A>(r: Ref<A>): Signal<A>;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@rhi-zone/rainbow",
3
+ "version": "0.1.0",
4
+ "description": "Optics-based reactivity — structured state for the web",
5
+ "type": "module",
6
+ "main": "./dist/rainbow.umd.cjs",
7
+ "module": "./dist/rainbow.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/rainbow.js",
12
+ "require": "./dist/rainbow.umd.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "dev": "vite build --watch",
19
+ "build": "vite build && tsgo --emitDeclarationOnly",
20
+ "typecheck": "tsgo --noEmit",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest"
23
+ },
24
+ "devDependencies": {
25
+ "@types/react": "^19.2.14",
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"
31
+ }
32
+ }