| /** | |
| * Mutable reactive references for local, in-memory state. | |
| * | |
| * `AtomRef` provides small observable state cells that can be read, updated, | |
| * mapped, and subscribed to without going through an `AtomRegistry`. Mutable | |
| * refs can also create refs for nested properties. The module also provides a | |
| * collection helper that stores item refs and notifies subscribers when items are | |
| * inserted, removed, or changed. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Equal from "../../Equal.ts" | |
| import * as Hash from "../../Hash.ts" | |
| /** | |
| * The literal type used to identify `AtomRef` values. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export type TypeId = "~effect/reactivity/AtomRef" | |
| /** | |
| * The runtime type id used to identify `AtomRef` values. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const TypeId: TypeId = "~effect/reactivity/AtomRef" | |
| /** | |
| * A read-only reactive reference. | |
| * | |
| * **Details** | |
| * | |
| * It exposes a stable key, the current value, subscriptions to value changes, and | |
| * `map` for creating derived read-only references. Equality and hashing are based | |
| * on the current value. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface ReadonlyRef<A> extends Equal.Equal { | |
| readonly [TypeId]: TypeId | |
| readonly key: string | |
| readonly value: A | |
| readonly subscribe: (f: (a: A) => void) => () => void | |
| readonly map: <B>(f: (a: A) => B) => ReadonlyRef<B> | |
| } | |
| /** | |
| * A mutable reactive reference. | |
| * | |
| * **Details** | |
| * | |
| * It supports replacing the whole value, updating it from the current value, and | |
| * creating mutable references to nested properties. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface AtomRef<A> extends ReadonlyRef<A> { | |
| readonly prop: <K extends keyof A>(prop: K) => AtomRef<A[K]> | |
| readonly set: (value: A) => AtomRef<A> | |
| readonly update: (f: (value: A) => A) => AtomRef<A> | |
| } | |
| /** | |
| * A reactive collection of mutable item references. | |
| * | |
| * **Details** | |
| * | |
| * The collection can push, insert, and remove item refs, and `toArray` returns the | |
| * current raw item values. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Collection<A> extends ReadonlyRef<ReadonlyArray<AtomRef<A>>> { | |
| readonly push: (item: A) => Collection<A> | |
| readonly insertAt: (index: number, item: A) => Collection<A> | |
| readonly remove: (ref: AtomRef<A>) => Collection<A> | |
| readonly toArray: () => Array<A> | |
| } | |
| /** | |
| * Creates a mutable reactive reference initialized with the supplied value. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const make = <A>(value: A): AtomRef<A> => new AtomRefImpl(value) | |
| /** | |
| * Creates a reactive collection from an iterable of initial item values. | |
| * | |
| * **Details** | |
| * | |
| * Each item is wrapped in an `AtomRef`, and changes to item refs notify the | |
| * collection subscribers. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const collection = <A>(items: Iterable<A>): Collection<A> => new CollectionImpl(items) | |
| const keyState = { | |
| count: 0, | |
| generate() { | |
| return `AtomRef-${this.count++}` | |
| } | |
| } | |
| class ReadonlyRefImpl<A> implements ReadonlyRef<A> { | |
| readonly [TypeId]: TypeId | |
| readonly key = keyState.generate() | |
| public value: A | |
| constructor(value: A) { | |
| this[TypeId] = TypeId | |
| this.value = value | |
| } | |
| [Equal.symbol](that: Equal.Equal) { | |
| return Equal.equals(this.value, (that as ReadonlyRef<A>).value) | |
| } | |
| [Hash.symbol]() { | |
| return Hash.hash(this.value) | |
| } | |
| listeners: Listener<A> | null = null | |
| notify(a: A) { | |
| let listener = this.listeners | |
| while (listener !== null) { | |
| listener.f(a) | |
| listener = listener.next | |
| } | |
| } | |
| subscribe(f: (a: A) => void): () => void { | |
| const listener: Listener<A> = { | |
| f, | |
| prev: null, | |
| next: this.listeners | |
| } | |
| if (this.listeners) { | |
| this.listeners.prev = listener | |
| } | |
| this.listeners = listener | |
| return () => { | |
| if (this.listeners === listener) { | |
| this.listeners = listener.next | |
| } | |
| if (listener.prev) { | |
| listener.prev.next = listener.next | |
| } | |
| if (listener.next) { | |
| listener.next.prev = listener.prev | |
| } | |
| } | |
| } | |
| map<B>(f: (a: A) => B): ReadonlyRef<B> { | |
| return new MapRefImpl(this, f) | |
| } | |
| } | |
| type Listener<A> = { | |
| readonly f: (a: A) => void | |
| prev: Listener<A> | null | |
| next: Listener<A> | null | |
| } | |
| class AtomRefImpl<A> extends ReadonlyRefImpl<A> implements AtomRef<A> { | |
| prop<K extends keyof A>(prop: K): AtomRef<A[K]> { | |
| return new PropRefImpl(this, prop) | |
| } | |
| set(value: A) { | |
| if (Equal.equals(value, this.value)) { | |
| return this | |
| } | |
| this.value = value | |
| this.notify(value) | |
| return this | |
| } | |
| update(f: (value: A) => A) { | |
| return this.set(f(this.value)) | |
| } | |
| } | |
| class MapRefImpl<A, B> implements ReadonlyRef<B> { | |
| readonly [TypeId]: TypeId | |
| readonly key = keyState.generate() | |
| readonly parent: ReadonlyRef<A> | |
| readonly transform: (a: A) => B | |
| constructor(parent: ReadonlyRef<A>, transform: (a: A) => B) { | |
| this[TypeId] = TypeId | |
| this.parent = parent | |
| this.transform = transform | |
| } | |
| [Equal.symbol](that: Equal.Equal) { | |
| return Equal.equals(this.value, (that as ReadonlyRef<B>).value) | |
| } | |
| [Hash.symbol]() { | |
| return Hash.hash(this.value) | |
| } | |
| get value() { | |
| return this.transform(this.parent.value) | |
| } | |
| subscribe(f: (a: B) => void): () => void { | |
| let previous = this.transform(this.parent.value) | |
| return this.parent.subscribe((a) => { | |
| const next = this.transform(a) | |
| if (Equal.equals(next, previous)) { | |
| return | |
| } | |
| previous = next | |
| f(next) | |
| }) | |
| } | |
| map<C>(f: (a: B) => C): ReadonlyRef<C> { | |
| return new MapRefImpl(this, f) | |
| } | |
| } | |
| class PropRefImpl<A, K extends keyof A> implements AtomRef<A[K]> { | |
| readonly [TypeId]: TypeId | |
| readonly key = keyState.generate() | |
| private previous: A[K] | |
| readonly parent: AtomRef<A> | |
| readonly _prop: K | |
| constructor(parent: AtomRef<A>, _prop: K) { | |
| this[TypeId] = TypeId | |
| this.parent = parent | |
| this._prop = _prop | |
| this.previous = parent.value[_prop] | |
| } | |
| [Equal.symbol](that: Equal.Equal) { | |
| return Equal.equals(this.value, (that as ReadonlyRef<A>).value) | |
| } | |
| [Hash.symbol]() { | |
| return Hash.hash(this.value) | |
| } | |
| get value() { | |
| if (this.parent.value && this._prop in (this.parent.value as any)) { | |
| this.previous = this.parent.value[this._prop] | |
| } | |
| return this.previous | |
| } | |
| subscribe(f: (a: A[K]) => void): () => void { | |
| let previous = this.value | |
| return this.parent.subscribe((a) => { | |
| if (!a || !(this._prop in (a as any))) { | |
| return | |
| } | |
| const next = a[this._prop] | |
| if (Equal.equals(next, previous)) { | |
| return | |
| } | |
| previous = next | |
| f(next) | |
| }) | |
| } | |
| map<C>(f: (a: A[K]) => C): ReadonlyRef<C> { | |
| return new MapRefImpl(this, f) | |
| } | |
| prop<CK extends keyof A[K]>(prop: CK): AtomRef<A[K][CK]> { | |
| return new PropRefImpl(this, prop) | |
| } | |
| set(value: A[K]): AtomRef<A[K]> { | |
| if (Array.isArray(this.parent.value)) { | |
| const newArray = this.parent.value.slice() | |
| newArray[this._prop as number] = value | |
| this.parent.set(newArray as A) | |
| } else { | |
| this.parent.set({ | |
| ...this.parent.value, | |
| [this._prop]: value | |
| }) | |
| } | |
| return this | |
| } | |
| update(f: (value: A[K]) => A[K]): AtomRef<A[K]> { | |
| if (Array.isArray(this.parent.value)) { | |
| const newArray = this.parent.value.slice() | |
| newArray[this._prop as number] = f(this.parent.value[this._prop]) | |
| this.parent.set(newArray as A) | |
| } else { | |
| this.parent.set({ | |
| ...this.parent.value, | |
| [this._prop]: f(this.parent.value[this._prop]) | |
| }) | |
| } | |
| return this | |
| } | |
| } | |
| class CollectionImpl<A> extends ReadonlyRefImpl<Array<AtomRef<A>>> implements Collection<A> { | |
| constructor(items: Iterable<A>) { | |
| super([]) | |
| for (const item of items) { | |
| this.value.push(this.makeRef(item)) | |
| } | |
| } | |
| makeRef(value: A) { | |
| const ref = new AtomRefImpl(value) | |
| const notify = (value: A) => { | |
| ref.notify(value) | |
| this.notify(this.value) | |
| } | |
| return new Proxy(ref, { | |
| get(target, p, _receiver) { | |
| if (p === "notify") { | |
| return notify | |
| } | |
| return target[p as keyof AtomRef<A>] | |
| } | |
| }) | |
| } | |
| push(item: A) { | |
| const ref = this.makeRef(item) | |
| this.value.push(ref) | |
| this.notify(this.value) | |
| return this | |
| } | |
| insertAt(index: number, item: A) { | |
| const ref = this.makeRef(item) | |
| this.value.splice(index, 0, ref) | |
| this.notify(this.value) | |
| return this | |
| } | |
| remove(ref: AtomRef<A>) { | |
| const index = this.value.indexOf(ref) | |
| if (index !== -1) { | |
| this.value.splice(index, 1) | |
| this.notify(this.value) | |
| } | |
| return this | |
| } | |
| toArray() { | |
| return this.value.map((ref) => ref.value) | |
| } | |
| } | |
Xet Storage Details
- Size:
- 8.69 kB
- Xet hash:
- 34d99446b84ee8726841a68b03aca25f9bf305e5c3b5b85e1f05526c46b8cb7e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.