@rlabs-inc/signals 0.1.0 → 1.0.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 CHANGED
@@ -1,18 +1,20 @@
1
1
  # @rlabs-inc/signals
2
2
 
3
- **Ultra-lightweight fine-grained reactivity for TypeScript**
3
+ **Production-grade fine-grained reactivity for TypeScript.**
4
4
 
5
- A standalone reactive primitives library inspired by Svelte 5 runes, Solid.js, and Vue reactivity. Zero dependencies. ~700 lines of pure TypeScript.
5
+ A complete standalone mirror of Svelte 5's reactivity system. No compiler needed, no DOM, works anywhere - Bun, Node, Deno, or browser.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Fine-grained reactivity** - Only effects that read a signal re-run when it changes
10
- - **Deep reactivity** - Objects and arrays are automatically deeply reactive
11
- - **Lazy deriveds** - Computed values only recompute when read
12
- - **Batching** - Group updates to prevent multiple re-runs
13
- - **Reactive collections** - ReactiveMap, ReactiveSet, and reactiveArray
14
- - **Zero dependencies** - Pure TypeScript, works everywhere
15
- - **Tiny** - ~5KB minified
9
+ - **True Fine-Grained Reactivity** - Changes to deeply nested properties only trigger effects that read that exact path
10
+ - **Per-Property Tracking** - Proxy-based deep reactivity with lazy signal creation per property
11
+ - **Three-State Dirty Tracking** - Efficient CLEAN/MAYBE_DIRTY/DIRTY propagation
12
+ - **Automatic Cleanup** - Effects clean up when disposed, no memory leaks
13
+ - **Batching** - Group updates to prevent redundant effect runs
14
+ - **Self-Referencing Effects** - Effects can write to their own dependencies
15
+ - **Infinite Loop Protection** - Throws after 1000 iterations to catch bugs
16
+ - **Reactive Collections** - ReactiveMap, ReactiveSet, ReactiveDate
17
+ - **TypeScript Native** - Full type safety with generics
16
18
 
17
19
  ## Installation
18
20
 
@@ -25,69 +27,84 @@ npm install @rlabs-inc/signals
25
27
  ## Quick Start
26
28
 
27
29
  ```typescript
28
- import { signal, effect, derived, state } from '@rlabs-inc/signals'
30
+ import { signal, derived, effect, flushSync } from '@rlabs-inc/signals'
29
31
 
30
- // Simple signal
32
+ // Create a signal
31
33
  const count = signal(0)
32
34
 
33
- // Effect runs when dependencies change
35
+ // Create a derived value
36
+ const doubled = derived(() => count.value * 2)
37
+
38
+ // Create an effect
34
39
  effect(() => {
35
- console.log('Count:', count.value)
40
+ console.log(`Count: ${count.value}, Doubled: ${doubled.value}`)
36
41
  })
37
- // Logs: "Count: 0"
38
42
 
39
- count.value = 1
40
- // Logs: "Count: 1"
43
+ // Flush to run effects synchronously
44
+ flushSync() // Logs: "Count: 0, Doubled: 0"
41
45
 
42
- // Derived values
43
- const doubled = derived(() => count.value * 2)
44
- console.log(doubled.value) // 2
46
+ // Update the signal
47
+ count.value = 5
48
+ flushSync() // Logs: "Count: 5, Doubled: 10"
49
+ ```
45
50
 
46
- // Deep reactivity with state()
47
- const user = state({
48
- name: 'John',
49
- address: {
50
- city: 'NYC'
51
- }
52
- })
51
+ ## API Reference
53
52
 
54
- effect(() => {
55
- console.log('City:', user.address.city)
56
- })
57
- // Logs: "City: NYC"
53
+ ### Signals
54
+
55
+ #### `signal<T>(initialValue: T, options?): WritableSignal<T>`
56
+
57
+ Create a reactive value with `.value` getter/setter.
58
58
 
59
- user.address.city = 'LA'
60
- // Logs: "City: LA"
59
+ ```typescript
60
+ const name = signal('John')
61
+ console.log(name.value) // 'John'
62
+ name.value = 'Jane' // Triggers effects
61
63
  ```
62
64
 
63
- ## API Reference
65
+ **Options:**
66
+ - `equals?: (a: T, b: T) => boolean` - Custom equality function
64
67
 
65
- ### signal(initial, options?)
68
+ #### `state<T extends object>(initialValue: T): T`
66
69
 
67
- Create a reactive signal that holds a value.
70
+ Create a deeply reactive object. No `.value` needed - access properties directly.
68
71
 
69
72
  ```typescript
70
- const count = signal(0)
71
- console.log(count.value) // 0
73
+ const user = state({ name: 'John', address: { city: 'NYC' } })
74
+ user.name = 'Jane' // Reactive
75
+ user.address.city = 'LA' // Also reactive, deeply
76
+ ```
72
77
 
73
- count.value = 5
74
- console.log(count.value) // 5
78
+ ### Derived Values
75
79
 
76
- // With custom equality
77
- const obj = signal({ x: 1 }, {
78
- equals: (a, b) => a.x === b.x
79
- })
80
+ #### `derived<T>(fn: () => T): DerivedSignal<T>`
81
+
82
+ Create a computed value that automatically updates when dependencies change.
83
+
84
+ ```typescript
85
+ const firstName = signal('John')
86
+ const lastName = signal('Doe')
87
+
88
+ const fullName = derived(() => `${firstName.value} ${lastName.value}`)
89
+ console.log(fullName.value) // 'John Doe'
80
90
  ```
81
91
 
82
- ### effect(fn)
92
+ Deriveds are:
93
+ - **Lazy** - Only computed when read
94
+ - **Cached** - Value is memoized until dependencies change
95
+ - **Pure** - Cannot write to signals inside (throws error)
96
+
97
+ ### Effects
83
98
 
84
- Create a reactive effect that re-runs when dependencies change.
99
+ #### `effect(fn: () => void | CleanupFn): DisposeFn`
100
+
101
+ Create a side effect that re-runs when dependencies change.
85
102
 
86
103
  ```typescript
87
- const name = signal('John')
104
+ const count = signal(0)
88
105
 
89
106
  const dispose = effect(() => {
90
- console.log('Hello,', name.value)
107
+ console.log('Count is:', count.value)
91
108
 
92
109
  // Optional cleanup function
93
110
  return () => {
@@ -95,213 +112,266 @@ const dispose = effect(() => {
95
112
  }
96
113
  })
97
114
 
98
- name.value = 'Jane' // Effect re-runs
99
-
100
- dispose() // Stop the effect
115
+ // Stop the effect
116
+ dispose()
101
117
  ```
102
118
 
103
- ### derived(fn, options?)
119
+ #### `effect.root(fn: () => T): DisposeFn`
104
120
 
105
- Create a computed/derived value that updates automatically.
121
+ Create an effect scope that can contain nested effects.
106
122
 
107
123
  ```typescript
108
- const count = signal(5)
109
- const doubled = derived(() => count.value * 2)
110
-
111
- console.log(doubled.value) // 10
112
-
113
- count.value = 10
114
- console.log(doubled.value) // 20
124
+ const dispose = effect.root(() => {
125
+ effect(() => { /* ... */ })
126
+ effect(() => { /* ... */ })
127
+ })
115
128
 
116
- // Alias: derived.by() (Svelte compatibility)
117
- const tripled = derived.by(() => count.value * 3)
129
+ // Disposes all nested effects
130
+ dispose()
118
131
  ```
119
132
 
120
- ### state(initial)
133
+ #### `effect.pre(fn: () => void): DisposeFn`
121
134
 
122
- Create a deeply reactive state object. All nested properties are automatically reactive.
135
+ Create an effect that runs synchronously (like `$effect.pre` in Svelte).
123
136
 
124
137
  ```typescript
125
- const app = state({
126
- user: {
127
- name: 'John',
128
- preferences: {
129
- theme: 'dark'
130
- }
131
- },
132
- items: [1, 2, 3]
133
- })
134
-
135
- effect(() => {
136
- console.log(app.user.preferences.theme)
138
+ effect.pre(() => {
139
+ // Runs immediately, no flushSync needed
137
140
  })
138
-
139
- app.user.preferences.theme = 'light' // Triggers effect
140
- app.items.push(4) // Arrays are reactive too
141
141
  ```
142
142
 
143
- ### batch(fn)
143
+ ### Batching & Scheduling
144
+
145
+ #### `batch(fn: () => T): T`
144
146
 
145
- Batch multiple updates into a single reaction cycle.
147
+ Batch multiple signal updates into a single effect run.
146
148
 
147
149
  ```typescript
148
150
  const a = signal(1)
149
151
  const b = signal(2)
150
152
 
151
- effect(() => {
152
- console.log('Sum:', a.value + b.value)
153
- })
154
- // Logs: "Sum: 3"
153
+ effect(() => console.log(a.value + b.value))
155
154
 
156
155
  batch(() => {
157
156
  a.value = 10
158
157
  b.value = 20
159
158
  })
160
- // Logs: "Sum: 30" (only once, not twice)
159
+ // Effect runs once with final values, not twice
161
160
  ```
162
161
 
163
- ### untrack(fn)
162
+ #### `flushSync<T>(fn?: () => T): T | undefined`
164
163
 
165
- Read signals without creating dependencies.
164
+ Synchronously flush all pending effects.
166
165
 
167
166
  ```typescript
168
- const a = signal(1)
169
- const b = signal(2)
167
+ count.value = 5
168
+ flushSync() // Effects run NOW, not on next microtask
169
+ ```
170
+
171
+ #### `tick(): Promise<void>`
172
+
173
+ Wait for the next update cycle.
174
+
175
+ ```typescript
176
+ count.value = 5
177
+ await tick() // Effects have run
178
+ ```
179
+
180
+ ### Utilities
181
+
182
+ #### `untrack<T>(fn: () => T): T`
170
183
 
184
+ Read signals without creating dependencies.
185
+
186
+ ```typescript
171
187
  effect(() => {
172
- console.log('a:', a.value) // Creates dependency
173
- untrack(() => {
174
- console.log('b:', b.value) // Does NOT create dependency
175
- })
188
+ const a = count.value // Creates dependency
189
+ const b = untrack(() => other.value) // No dependency
176
190
  })
177
-
178
- b.value = 100 // Effect does NOT re-run
179
- a.value = 100 // Effect re-runs
180
191
  ```
181
192
 
182
- ### watch(source, callback, options?)
193
+ #### `peek<T>(signal: Source<T>): T`
194
+
195
+ Read a signal's value without tracking (low-level).
196
+
197
+ ### Deep Reactivity
198
+
199
+ #### `proxy<T extends object>(value: T): T`
183
200
 
184
- Watch a signal and call callback when it changes.
201
+ Create a deeply reactive proxy (used internally by `state()`).
185
202
 
186
203
  ```typescript
187
- const count = signal(0)
204
+ const obj = proxy({ a: { b: { c: 1 } } })
205
+ obj.a.b.c = 2 // Only triggers effects reading a.b.c
206
+ ```
188
207
 
189
- const dispose = watch(
190
- () => count.value,
191
- (newValue, oldValue) => {
192
- console.log(`Changed from ${oldValue} to ${newValue}`)
193
- }
194
- )
208
+ #### `toRaw<T>(value: T): T`
195
209
 
196
- count.value = 1 // Logs: "Changed from 0 to 1"
210
+ Get the original object from a proxy.
197
211
 
198
- // With immediate option
199
- watch(
200
- () => count.value,
201
- (value) => console.log('Value:', value),
202
- { immediate: true }
203
- )
204
- // Logs immediately: "Value: 1"
212
+ ```typescript
213
+ const raw = toRaw(user) // Original non-reactive object
205
214
  ```
206
215
 
207
- ### ReactiveMap
216
+ #### `isReactive(value: unknown): boolean`
208
217
 
209
- A reactive Map that triggers updates on modifications.
218
+ Check if a value is a reactive proxy.
219
+
220
+ ### Reactive Collections
221
+
222
+ #### `ReactiveMap<K, V>`
223
+
224
+ A Map with per-key reactivity.
210
225
 
211
226
  ```typescript
212
- const map = new ReactiveMap<string, number>()
227
+ const users = new ReactiveMap<string, User>()
213
228
 
214
229
  effect(() => {
215
- console.log('Value:', map.get('key'))
230
+ console.log(users.get('john')) // Only re-runs when 'john' changes
216
231
  })
217
232
 
218
- map.set('key', 42) // Triggers effect
233
+ users.set('jane', { name: 'Jane' }) // Doesn't trigger above effect
219
234
  ```
220
235
 
221
- ### ReactiveSet
236
+ #### `ReactiveSet<T>`
222
237
 
223
- A reactive Set that triggers updates on modifications.
238
+ A Set with per-item reactivity.
224
239
 
225
240
  ```typescript
226
- const set = new ReactiveSet<string>()
241
+ const tags = new ReactiveSet<string>()
227
242
 
228
243
  effect(() => {
229
- console.log('Has item:', set.has('item'))
244
+ console.log(tags.has('important')) // Only re-runs when 'important' changes
230
245
  })
231
-
232
- set.add('item') // Triggers effect
233
246
  ```
234
247
 
235
- ### reactiveArray(initial?)
248
+ #### `ReactiveDate`
236
249
 
237
- Create a reactive array with fine-grained reactivity.
250
+ A Date with reactive getters/setters.
238
251
 
239
252
  ```typescript
240
- const items = reactiveArray([1, 2, 3])
253
+ const date = new ReactiveDate()
241
254
 
242
255
  effect(() => {
243
- console.log('First:', items[0])
244
- console.log('Length:', items.length)
256
+ console.log(date.getHours()) // Re-runs when time changes
245
257
  })
246
258
 
247
- items[0] = 10 // Triggers effect
248
- items.push(4) // Triggers effect
259
+ date.setHours(12) // Triggers effect
249
260
  ```
250
261
 
251
- ### Utilities
262
+ ## Advanced Usage
263
+
264
+ ### Self-Referencing Effects
265
+
266
+ Effects can write to signals they depend on:
252
267
 
253
268
  ```typescript
254
- // Create read-only view
255
269
  const count = signal(0)
256
- const ro = readonly(count)
257
- // ro.value = 5 // TypeScript error
258
270
 
259
- // Read without tracking (alias for untrack)
260
- const value = peek(() => count.value)
271
+ effect(() => {
272
+ if (count.value < 10) {
273
+ count.value++ // Will re-run until count reaches 10
274
+ }
275
+ })
276
+ ```
261
277
 
262
- // Check if value is reactive
263
- isReactive(someObject)
278
+ **Note:** Unguarded self-references throw after 1000 iterations.
264
279
 
265
- // Get raw object from reactive proxy
266
- const raw = toRaw(reactiveObject)
280
+ ### Custom Equality
267
281
 
268
- // Create effect scope
269
- const scope = effectScope()
270
- scope.run(() => {
271
- effect(() => { /* ... */ })
272
- effect(() => { /* ... */ })
282
+ ```typescript
283
+ import { signal, shallowEquals } from '@rlabs-inc/signals'
284
+
285
+ const obj = signal({ a: 1 }, { equals: shallowEquals })
286
+ obj.value = { a: 1 } // Won't trigger - shallowly equal
287
+ ```
288
+
289
+ Built-in equality functions:
290
+ - `equals` - Default, uses `Object.is`
291
+ - `safeEquals` - Handles NaN correctly
292
+ - `shallowEquals` - Shallow object comparison
293
+ - `neverEquals` - Always triggers (always false)
294
+ - `alwaysEquals` - Never triggers (always true)
295
+
296
+ ### Low-Level API
297
+
298
+ For advanced use cases, you can access internal primitives:
299
+
300
+ ```typescript
301
+ import { source, get, set } from '@rlabs-inc/signals'
302
+
303
+ // Create a raw source (no .value wrapper)
304
+ const src = source(0)
305
+
306
+ // Read with tracking
307
+ const value = get(src)
308
+
309
+ // Write with notification
310
+ set(src, 10)
311
+ ```
312
+
313
+ ## Error Handling
314
+
315
+ ### "Cannot write to signals inside a derived"
316
+
317
+ Deriveds must be pure computations:
318
+
319
+ ```typescript
320
+ // BAD - will throw
321
+ const bad = derived(() => {
322
+ otherSignal.value = 10 // Throws!
323
+ return count.value
324
+ })
325
+
326
+ // GOOD - use effects for side effects
327
+ effect(() => {
328
+ if (count.value > 0) {
329
+ otherSignal.value = count.value * 2
330
+ }
273
331
  })
274
- scope.stop() // Disposes all effects
275
332
  ```
276
333
 
277
- ## Comparison with Svelte 5
334
+ ### "Maximum update depth exceeded"
278
335
 
279
- | Svelte 5 | @rlabs-inc/signals |
280
- |----------|-------------------|
281
- | `$state(value)` | `signal(value)` or `state(obj)` |
282
- | `$derived(expr)` | `derived(() => expr)` |
283
- | `$derived.by(fn)` | `derived.by(fn)` or `derived(fn)` |
284
- | `$effect(fn)` | `effect(fn)` |
285
- | `SvelteMap` | `ReactiveMap` |
286
- | `SvelteSet` | `ReactiveSet` |
336
+ Your effect is infinitely re-triggering itself:
287
337
 
288
- **Key differences:**
289
- - No compiler needed - works with plain TypeScript
290
- - Use `.value` to read/write signals (like Vue/Solid)
291
- - No Happy DOM or browser environment required
338
+ ```typescript
339
+ // BAD - infinite loop
340
+ effect(() => {
341
+ count.value = count.value + 1 // Always triggers itself
342
+ })
343
+
344
+ // GOOD - add a guard
345
+ effect(() => {
346
+ if (count.value < 100) {
347
+ count.value++
348
+ }
349
+ })
350
+ ```
292
351
 
293
- ## Why?
352
+ ## Performance
294
353
 
295
- We built this because:
296
- 1. We love Svelte 5's reactivity model
297
- 2. We wanted to use it outside Svelte components
298
- 3. We didn't want the overhead of Happy DOM for server-side usage
299
- 4. We needed a lightweight solution for libraries like [FatherStateDB](https://github.com/rlabs-inc/fatherstatedb)
354
+ This library is designed for performance:
300
355
 
301
- ## License
356
+ - **Lazy evaluation** - Deriveds only compute when read
357
+ - **Version-based deduplication** - No duplicate dependency tracking
358
+ - **Linked list effect tree** - O(1) effect insertion/removal
359
+ - **Microtask batching** - Updates coalesce automatically
360
+ - **Per-property signals** - Fine-grained updates at any depth
302
361
 
303
- MIT
362
+ ## Comparison with Svelte 5
304
363
 
305
- ---
364
+ | Feature | Svelte 5 | @rlabs-inc/signals |
365
+ |---------|----------|-------------------|
366
+ | Compiler required | Yes | No |
367
+ | DOM integration | Yes | No |
368
+ | Fine-grained reactivity | Yes | Yes |
369
+ | Deep proxy reactivity | Yes | Yes |
370
+ | Batching | Yes | Yes |
371
+ | Effect cleanup | Yes | Yes |
372
+ | TypeScript | Yes | Yes |
373
+ | Runs in Node/Bun | Needs adapter | Native |
306
374
 
307
- Built with ❤️ by RLabs Inc.
375
+ ## License
376
+
377
+ MIT
@@ -0,0 +1,67 @@
1
+ /**
2
+ * A reactive Date
3
+ *
4
+ * All getters are reactive - they track changes to the underlying time.
5
+ * All setters trigger updates.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const date = new ReactiveDate()
10
+ *
11
+ * effect(() => {
12
+ * console.log('Hours:', date.getHours())
13
+ * })
14
+ *
15
+ * date.setHours(12) // Triggers effect
16
+ * ```
17
+ */
18
+ export declare class ReactiveDate extends Date {
19
+ #private;
20
+ constructor();
21
+ constructor(value: number | string | Date);
22
+ constructor(year: number, monthIndex: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number);
23
+ getTime(): number;
24
+ getFullYear(): number;
25
+ getMonth(): number;
26
+ getDate(): number;
27
+ getDay(): number;
28
+ getHours(): number;
29
+ getMinutes(): number;
30
+ getSeconds(): number;
31
+ getMilliseconds(): number;
32
+ getUTCFullYear(): number;
33
+ getUTCMonth(): number;
34
+ getUTCDate(): number;
35
+ getUTCDay(): number;
36
+ getUTCHours(): number;
37
+ getUTCMinutes(): number;
38
+ getUTCSeconds(): number;
39
+ getUTCMilliseconds(): number;
40
+ getTimezoneOffset(): number;
41
+ setTime(time: number): number;
42
+ setFullYear(year: number, month?: number, date?: number): number;
43
+ setMonth(month: number, date?: number): number;
44
+ setDate(date: number): number;
45
+ setHours(hours: number, min?: number, sec?: number, ms?: number): number;
46
+ setMinutes(min: number, sec?: number, ms?: number): number;
47
+ setSeconds(sec: number, ms?: number): number;
48
+ setMilliseconds(ms: number): number;
49
+ setUTCFullYear(year: number, month?: number, date?: number): number;
50
+ setUTCMonth(month: number, date?: number): number;
51
+ setUTCDate(date: number): number;
52
+ setUTCHours(hours: number, min?: number, sec?: number, ms?: number): number;
53
+ setUTCMinutes(min: number, sec?: number, ms?: number): number;
54
+ setUTCSeconds(sec: number, ms?: number): number;
55
+ setUTCMilliseconds(ms: number): number;
56
+ toString(): string;
57
+ toDateString(): string;
58
+ toTimeString(): string;
59
+ toISOString(): string;
60
+ toUTCString(): string;
61
+ toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
62
+ toLocaleDateString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
63
+ toLocaleTimeString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
64
+ toJSON(): string;
65
+ valueOf(): number;
66
+ }
67
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/collections/date.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A reactive Map with per-key granularity
3
+ *
4
+ * Three levels of reactivity:
5
+ * 1. Per-key signals: map.get('key') only tracks that specific key
6
+ * 2. Version signal: Tracks structural changes (add/delete)
7
+ * 3. Size signal: Tracks map size changes
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const users = new ReactiveMap<string, User>()
12
+ *
13
+ * effect(() => {
14
+ * // Only re-runs when 'alice' changes
15
+ * console.log(users.get('alice'))
16
+ * })
17
+ *
18
+ * users.set('bob', { name: 'Bob' }) // Doesn't trigger above effect
19
+ * users.set('alice', { name: 'Alice Updated' }) // Triggers effect
20
+ * ```
21
+ */
22
+ export declare class ReactiveMap<K, V> extends Map<K, V> {
23
+ #private;
24
+ constructor(entries?: Iterable<readonly [K, V]> | null);
25
+ get size(): number;
26
+ has(key: K): boolean;
27
+ get(key: K): V | undefined;
28
+ set(key: K, value: V): this;
29
+ delete(key: K): boolean;
30
+ clear(): void;
31
+ keys(): MapIterator<K>;
32
+ values(): MapIterator<V>;
33
+ entries(): MapIterator<[K, V]>;
34
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void;
35
+ [Symbol.iterator](): MapIterator<[K, V]>;
36
+ }
37
+ //# sourceMappingURL=map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/collections/map.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,WAAW,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;;gBAUlC,OAAO,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI;IA4BtD,IAAI,IAAI,IAAI,MAAM,CAGjB;IAMD,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAwBpB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IA0B1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAsC3B,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAwBvB,KAAK,IAAI,IAAI;IAmBb,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC;IAKtB,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC;IAKxB,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAK9B,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;IAKxF,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAGzC"}