value-enhancer
Advanced tools
Comparing version 5.1.3 to 5.2.0
@@ -1,2 +0,63 @@ | ||
import { R as ReadonlyVal } from './typings-e10eb4db.js'; | ||
/** | ||
* A ReadonlyVal contains a readonly `value` and does not have a `set` method. | ||
*/ | ||
interface ReadonlyVal<TValue = any> { | ||
/** | ||
* Current value of the val. | ||
*/ | ||
readonly value: TValue; | ||
/** | ||
* A version representation of the value. | ||
* If two versions of a val is not equal(`Object.is`), it means the `value` has changed (event if the `value` is equal). | ||
*/ | ||
readonly $version: ValVersion; | ||
/** | ||
* Get current value of the val. | ||
*/ | ||
get: () => TValue; | ||
/** | ||
* Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. | ||
* (It is just like `derive` a val without `transform`. It is simpler hence more efficient.) | ||
* All ref ReadonlyVals share the same value from the source ReadonlyVal. | ||
* | ||
* With this pattern you can pass a ref ReadonlyVal to downstream. | ||
* The ref ReadonlyVals can be safely disposed without affecting the source ReadonlyVal and other ref ReadonlyVals. | ||
*/ | ||
ref(): ReadonlyVal<TValue>; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
reaction(subscriber: ValSubscriber<TValue>, eager?: boolean): ValDisposer; | ||
/** | ||
* Subscribe to value changes with immediate emission. | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
subscribe(subscriber: ValSubscriber<TValue>, eager?: boolean): ValDisposer; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* The subscribers will be called before sync and async subscribers from [[reaction]] and [[subscribe]]. | ||
* It is mainly used for chaining Vals. | ||
* @param subscriber | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
$valCompute(subscriber: ValSubscriber<void>): ValDisposer; | ||
/** | ||
* Remove the given subscriber. | ||
* Remove all if no subscriber provided. | ||
* @param subscriber | ||
*/ | ||
unsubscribe(subscriber?: (...args: any[]) => any): void; | ||
/** | ||
* Remove all subscribers. | ||
*/ | ||
dispose(): void; | ||
} | ||
type ValSubscriber<TValue = any> = (newValue: TValue) => void; | ||
type ValDisposer = () => void; | ||
type ValVersion = any; | ||
@@ -3,0 +64,0 @@ /** |
'use strict'; | ||
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js'); | ||
var _ = require('.'); | ||
@@ -8,3 +8,3 @@ // src/collections/list.ts | ||
constructor(items) { | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal( | ||
const [$, set$] = _.readonlyVal( | ||
items ? [...items] : [], | ||
@@ -92,3 +92,3 @@ { equal: false } | ||
set(index, item) { | ||
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) { | ||
if (index >= 0 && !_.strictEqual(this.array[index], item)) { | ||
this.array[index] = item; | ||
@@ -102,3 +102,3 @@ this.#notify(); | ||
for (const [index, item] of entries) { | ||
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) { | ||
if (index >= 0 && !_.strictEqual(this.array[index], item)) { | ||
isDirty = true; | ||
@@ -175,4 +175,2 @@ this.array[index] = item; | ||
var reactiveList = (items) => new ReactiveListImpl(items); | ||
// src/collections/map.ts | ||
var ReactiveMapImpl = class extends Map { | ||
@@ -184,3 +182,3 @@ constructor(entries, config) { | ||
} | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false }); | ||
const [$, set$] = _.readonlyVal(this, { equal: false }); | ||
this.$ = $; | ||
@@ -243,3 +241,3 @@ this.#notify = () => set$(this); | ||
const oldValue = this.get(key); | ||
if (chunkJNRMXROO_js.strictEqual(oldValue, value)) { | ||
if (_.strictEqual(oldValue, value)) { | ||
return false; | ||
@@ -309,4 +307,2 @@ } | ||
var reactiveMap = (entries, config) => new ReactiveMapImpl(entries, config); | ||
// src/collections/set.ts | ||
var ReactiveSetImpl = class extends Set { | ||
@@ -318,3 +314,3 @@ constructor(values, config) { | ||
} | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false }); | ||
const [$, set$] = _.readonlyVal(this, { equal: false }); | ||
this.$ = $; | ||
@@ -321,0 +317,0 @@ this.#notify = () => set$(this); |
@@ -1,3 +0,130 @@ | ||
import { R as ReadonlyVal, V as ValInputsValueTuple, a as ValConfig, U as UnwrapVal, b as ValDisposer, c as ValSubscriber, d as Val, N as NoInfer, e as ValSetValue } from './typings-e10eb4db.js'; | ||
export { F as FlattenVal, f as ValEqual, g as ValVersion } from './typings-e10eb4db.js'; | ||
/** | ||
* A ReadonlyVal contains a readonly `value` and does not have a `set` method. | ||
*/ | ||
interface ReadonlyVal<TValue = any> { | ||
/** | ||
* Current value of the val. | ||
*/ | ||
readonly value: TValue; | ||
/** | ||
* A version representation of the value. | ||
* If two versions of a val is not equal(`Object.is`), it means the `value` has changed (event if the `value` is equal). | ||
*/ | ||
readonly $version: ValVersion; | ||
/** | ||
* Get current value of the val. | ||
*/ | ||
get: () => TValue; | ||
/** | ||
* Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. | ||
* (It is just like `derive` a val without `transform`. It is simpler hence more efficient.) | ||
* All ref ReadonlyVals share the same value from the source ReadonlyVal. | ||
* | ||
* With this pattern you can pass a ref ReadonlyVal to downstream. | ||
* The ref ReadonlyVals can be safely disposed without affecting the source ReadonlyVal and other ref ReadonlyVals. | ||
*/ | ||
ref(): ReadonlyVal<TValue>; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
reaction(subscriber: ValSubscriber<TValue>, eager?: boolean): ValDisposer; | ||
/** | ||
* Subscribe to value changes with immediate emission. | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
subscribe(subscriber: ValSubscriber<TValue>, eager?: boolean): ValDisposer; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* The subscribers will be called before sync and async subscribers from [[reaction]] and [[subscribe]]. | ||
* It is mainly used for chaining Vals. | ||
* @param subscriber | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
$valCompute(subscriber: ValSubscriber<void>): ValDisposer; | ||
/** | ||
* Remove the given subscriber. | ||
* Remove all if no subscriber provided. | ||
* @param subscriber | ||
*/ | ||
unsubscribe(subscriber?: (...args: any[]) => any): void; | ||
/** | ||
* Remove all subscribers. | ||
*/ | ||
dispose(): void; | ||
} | ||
/** | ||
* A Val contains a writable `value` property and a `set` method. | ||
*/ | ||
interface Val<TValue = any> extends ReadonlyVal<TValue> { | ||
/** Current value of the val */ | ||
value: TValue; | ||
/** Set new value */ | ||
set: (value: TValue) => void; | ||
/** | ||
* Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. | ||
* (It is just like `derive` a val without `transform`. It is simpler hence more efficient.) | ||
* All ref ReadonlyVals share the same value from the source ReadonlyVal. | ||
* | ||
* With this pattern you can pass a ref ReadonlyVal to downstream. | ||
* The ref ReadonlyVals can be safely disposed without affecting the source ReadonlyVal and other ref ReadonlyVals. | ||
*/ | ||
ref(): ReadonlyVal<TValue>; | ||
/** | ||
* Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. | ||
* (It is just like `derive` a val without `transform`. It is simpler hence more efficient.) | ||
* All ref ReadonlyVals share the same value from the source ReadonlyVal. | ||
* | ||
* With this pattern you can pass a ref ReadonlyVal to downstream. | ||
* The ref ReadonlyVals can be safely disposed without affecting the source ReadonlyVal and other ref ReadonlyVals. | ||
*/ | ||
ref(writable?: false): ReadonlyVal<TValue>; | ||
/** | ||
* Create a new Val referencing the value of the current Val as source. | ||
* All ref Vals share the same value from the source Val. | ||
* The act of setting a value on the ref Val is essentially setting the value on the source Val. | ||
* | ||
* With this pattern you can pass a ref Val as a writable Val to downstream. | ||
* The ref Vals can be safely disposed without affecting the source Val and other ref Vals. | ||
*/ | ||
ref(writable: true): Val<TValue>; | ||
/** | ||
* @param writable If true, creates a new Ref Val referencing the value of the current Val as source. | ||
* If false, creates a new Ref ReadonlyVal referencing the value of the current Val as source. | ||
*/ | ||
ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue>; | ||
} | ||
type ValSetValue<TValue = any> = (value: TValue) => void; | ||
type ValEqual<TValue = any> = (newValue: TValue, oldValue: TValue) => boolean; | ||
type ValSubscriber<TValue = any> = (newValue: TValue) => void; | ||
type ValDisposer = () => void; | ||
/** | ||
* Custom config for the val. | ||
*/ | ||
interface ValConfig<TValue = any> { | ||
/** | ||
* Compare two values. Default `Object.is`. | ||
* `false` to disable equality check. | ||
*/ | ||
readonly equal?: ValEqual<TValue> | false; | ||
/** | ||
* Set the default behavior of subscription and reaction. | ||
* Emission triggers synchronously if `true`. Default `false`. | ||
*/ | ||
readonly eager?: boolean; | ||
} | ||
type ValVersion = any; | ||
type UnwrapVal<T> = T extends ReadonlyVal<infer TValue> ? TValue : T; | ||
type FlattenVal<T> = ReadonlyVal<UnwrapVal<UnwrapVal<T>>>; | ||
/** @internal */ | ||
type ValInputsValueTuple<TValInputs extends readonly ReadonlyVal[]> = Readonly<{ | ||
[K in keyof TValInputs]: ExtractValValue<TValInputs[K]>; | ||
}>; | ||
/** @internal */ | ||
type ExtractValValue<TVal> = TVal extends ReadonlyVal<infer TValue> ? TValue : never; | ||
type NoInfer<T> = [T][T extends any ? 0 : never]; | ||
@@ -134,2 +261,3 @@ type CombineValTransform<TCombinedValue = any, TValues extends readonly any[] = any[]> = (newValues: TValues) => TCombinedValue; | ||
* @deprecated | ||
* @ignore | ||
* Set the value of a val. | ||
@@ -186,2 +314,6 @@ * It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`). | ||
declare const attachSetter: <TValue>(val$: ReadonlyVal<TValue>, set: (this: void, value: TValue) => void) => Val<TValue>; | ||
interface IsVal { | ||
<T extends ReadonlyVal>(val$: T): val$ is T extends ReadonlyVal ? T : never; | ||
(val$: unknown): val$ is ReadonlyVal; | ||
} | ||
/** | ||
@@ -192,9 +324,8 @@ * Checks if `val` is `ReadonlyVal` or `Val`. | ||
*/ | ||
declare function isVal<T extends ReadonlyVal>(val: T): val is T extends ReadonlyVal ? T : never; | ||
declare const isVal: IsVal; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
* Checks if `val` is a writable `Val`. | ||
* @returns `true` if `val` is a writable `Val`. | ||
*/ | ||
declare function isVal(val: unknown): val is ReadonlyVal; | ||
declare const isWritable: <TValue>(val$: ReadonlyVal<TValue>) => val$ is Val<TValue>; | ||
@@ -304,2 +435,2 @@ /** | ||
export { CombineValTransform, DerivedValTransform, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, arrayShallowEqual, attachSetter, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, nextTick, reaction, readonlyVal, setValue, strictEqual, subscribe, unsubscribe, val }; | ||
export { CombineValTransform, DerivedValTransform, FlattenVal, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValEqual, ValSetValue, ValSubscriber, ValVersion, arrayShallowEqual, attachSetter, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, isWritable, nextTick, reaction, readonlyVal, setValue, strictEqual, subscribe, unsubscribe, val }; |
'use strict'; | ||
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js'); | ||
// src/utils.ts | ||
var INIT_VALUE = {}; | ||
var setValue = (val2, value) => val2?.set?.(value); | ||
var subscribe = (val2, subscriber, eager) => val2.subscribe(subscriber, eager); | ||
var reaction = (val2, subscriber, eager) => val2.reaction(subscriber, eager); | ||
var unsubscribe = (val2, subscriber) => val2.unsubscribe(subscriber); | ||
var identity = (value) => value; | ||
var strictEqual = Object.is; | ||
var arrayShallowEqual = (arrA, arrB) => { | ||
if (strictEqual(arrA, arrB)) { | ||
return true; | ||
} | ||
if (!Array.isArray(arrA) || !Array.isArray(arrB)) { | ||
return false; | ||
} | ||
const len = arrA.length; | ||
if (arrB.length !== len) { | ||
return false; | ||
} | ||
for (let i = 0; i < len; i++) { | ||
if (!strictEqual(arrA[i], arrB[i])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
var getValue = (val2) => val2.value; | ||
var getValues = (valInputs) => valInputs.map(getValue); | ||
var getValVersion = (val$) => val$.$version; | ||
var invoke = (fn, value) => { | ||
try { | ||
fn(value); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
}; | ||
var attachSetter = (val$, set) => (val$.set = set, val$); | ||
var isVal = (val$) => !!val$?.$valCompute; | ||
var isWritable = (val$) => !!val$?.set; | ||
// src/dev/customFormatter.ts | ||
function initCustomFormatter() { | ||
if (typeof window === "undefined") { | ||
return; | ||
} | ||
const valStyle = { style: "color:#a225c2" }; | ||
const numberStyle = { style: "color:#268bd2" }; | ||
const stringStyle = { style: "color:#e28c5f" }; | ||
const keywordStyle = { style: "color:#eb2f96" }; | ||
const formatter = { | ||
header: (obj) => isVal(obj) ? [ | ||
"div", | ||
valStyle, | ||
`${isWritable(obj) ? "Val" : "ReadonlyVal"}`, | ||
`<`, | ||
formatValue(obj.value), | ||
`>` | ||
] : null, | ||
hasBody: () => false | ||
}; | ||
function formatValue(v) { | ||
if (typeof v === "number") { | ||
return ["span", numberStyle, v]; | ||
} else if (typeof v === "string") { | ||
return ["span", stringStyle, v]; | ||
} else if (typeof v === "boolean") { | ||
return ["span", keywordStyle, v]; | ||
} else if (typeof v === "object" && v !== null) { | ||
return ["object", { object: v }]; | ||
} else { | ||
return ["span", stringStyle, String(v)]; | ||
} | ||
} | ||
(window.devtoolsFormatters ??= []).push(formatter); | ||
} | ||
// src/dev/index.ts | ||
function initDev() { | ||
initCustomFormatter(); | ||
} | ||
Object.defineProperty(exports, 'arrayShallowEqual', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.arrayShallowEqual; } | ||
}); | ||
Object.defineProperty(exports, 'attachSetter', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.attachSetter; } | ||
}); | ||
Object.defineProperty(exports, 'combine', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.combine; } | ||
}); | ||
Object.defineProperty(exports, 'derive', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.derive; } | ||
}); | ||
Object.defineProperty(exports, 'flatten', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.flatten; } | ||
}); | ||
Object.defineProperty(exports, 'flattenFrom', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.flattenFrom; } | ||
}); | ||
Object.defineProperty(exports, 'from', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.from; } | ||
}); | ||
Object.defineProperty(exports, 'groupVals', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.groupVals; } | ||
}); | ||
Object.defineProperty(exports, 'identity', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.identity; } | ||
}); | ||
Object.defineProperty(exports, 'isVal', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.isVal; } | ||
}); | ||
Object.defineProperty(exports, 'nextTick', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.nextTick; } | ||
}); | ||
Object.defineProperty(exports, 'reaction', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.reaction; } | ||
}); | ||
Object.defineProperty(exports, 'readonlyVal', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.readonlyVal; } | ||
}); | ||
Object.defineProperty(exports, 'setValue', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.setValue; } | ||
}); | ||
Object.defineProperty(exports, 'strictEqual', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.strictEqual; } | ||
}); | ||
Object.defineProperty(exports, 'subscribe', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.subscribe; } | ||
}); | ||
Object.defineProperty(exports, 'unsubscribe', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.unsubscribe; } | ||
}); | ||
Object.defineProperty(exports, 'val', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.val; } | ||
}); | ||
// src/scheduler.ts | ||
var tick = /* @__PURE__ */ Promise.resolve(); | ||
var pendingTasks1 = /* @__PURE__ */ new Set(); | ||
var pendingTasks2 = /* @__PURE__ */ new Set(); | ||
var pendingTasks = pendingTasks1; | ||
var pending; | ||
var flush = () => { | ||
const currentPendingTasks = pendingTasks; | ||
pendingTasks = pendingTasks === pendingTasks1 ? pendingTasks2 : pendingTasks1; | ||
pending = false; | ||
for (const subs of currentPendingTasks) { | ||
subs.t(); | ||
} | ||
currentPendingTasks.clear(); | ||
}; | ||
var schedule = (task) => { | ||
pendingTasks.add(task); | ||
pending = pending || tick.then(flush); | ||
}; | ||
var cancelTask = (task) => pendingTasks.delete(task); | ||
var nextTick = () => tick; | ||
// src/agent.ts | ||
var ValAgent = class { | ||
constructor(getValue2, config, onStart) { | ||
this.#onStart = onStart; | ||
this.#getValue = getValue2; | ||
this.q = (config?.equal ?? strictEqual) || void 0; | ||
this.e = config?.eager; | ||
} | ||
s = /* @__PURE__ */ new Map(); | ||
b = 2 /* ValueDirty */; | ||
v = INIT_VALUE; | ||
c = INIT_VALUE; | ||
q; | ||
e; | ||
u = () => { | ||
if (this.b & 2 /* ValueDirty */) { | ||
const newValue = this.#getValue(); | ||
if (this.c === INIT_VALUE) { | ||
this.d(newValue); | ||
} else if (!this.q?.(newValue, this.c)) { | ||
this.d(newValue); | ||
this.b |= 12 /* ShouldInvoke */; | ||
} | ||
if (this.s.size) { | ||
this.b &= ~2 /* ValueDirty */; | ||
} | ||
} | ||
this.b &= ~1 /* Notified */; | ||
return this.c; | ||
}; | ||
n = () => { | ||
this.b |= 2 /* ValueDirty */; | ||
if (!(this.b & 1 /* Notified */)) { | ||
this.b |= 1 /* Notified */; | ||
if (this[3 /* Computed */]) { | ||
this.#invoke(3 /* Computed */); | ||
} | ||
if (this[2 /* Eager */]) { | ||
this.u(); | ||
if (this.b & 4 /* ShouldInvokeEager */) { | ||
this.b &= ~4 /* ShouldInvokeEager */; | ||
this.#invoke(2 /* Eager */); | ||
} else { | ||
this.b &= ~12 /* ShouldInvoke */; | ||
return; | ||
} | ||
} else { | ||
this.b &= ~4 /* ShouldInvokeEager */; | ||
} | ||
if (this[1 /* Async */]) { | ||
schedule(this); | ||
} else { | ||
this.b &= ~8 /* ShouldInvokeAsync */; | ||
} | ||
} | ||
}; | ||
a(subscriber, mode) { | ||
const oldSize = this.s.size; | ||
const currentMode = this.s.get(subscriber); | ||
if (currentMode) { | ||
this[currentMode]--; | ||
} | ||
this.s.set(subscriber, mode); | ||
this[mode]++; | ||
if (!oldSize) { | ||
this.u(); | ||
this.b &= ~12 /* ShouldInvoke */; | ||
this.#onStartDisposer?.(); | ||
this.#onStartDisposer = this.#onStart?.(this.n); | ||
} | ||
return () => this.r(subscriber); | ||
} | ||
r(subscriber) { | ||
if (subscriber) { | ||
const mode = this.s.get(subscriber); | ||
if (mode) { | ||
this.s.delete(subscriber); | ||
this[mode]--; | ||
} | ||
} else { | ||
this.s.clear(); | ||
this[1 /* Async */] = this[2 /* Eager */] = this[3 /* Computed */] = 0; | ||
cancelTask(this); | ||
} | ||
if (!this.s.size) { | ||
this.b |= 2 /* ValueDirty */; | ||
if (this.#onStartDisposer) { | ||
this.#onStartDisposer(); | ||
this.#onStartDisposer = null; | ||
} | ||
} | ||
} | ||
t() { | ||
if (this[1 /* Async */]) { | ||
this.u(); | ||
if (this.b & 8 /* ShouldInvokeAsync */) { | ||
this.b &= ~8 /* ShouldInvokeAsync */; | ||
this.#invoke(1 /* Async */); | ||
} | ||
} else { | ||
this.b &= ~8 /* ShouldInvokeAsync */; | ||
} | ||
} | ||
d(value) { | ||
this.d = this.q?.(value, value) ? this.f : this.g; | ||
this.d(value); | ||
} | ||
g(value) { | ||
this.c = value; | ||
this.v = this.#numberVersion++ | 0; | ||
} | ||
f(value) { | ||
this.c = this.v = value; | ||
} | ||
[1 /* Async */] = 0; | ||
[2 /* Eager */] = 0; | ||
[3 /* Computed */] = 0; | ||
#numberVersion = 0; | ||
#getValue; | ||
#onStart; | ||
#onStartDisposer; | ||
#invoke(mode) { | ||
for (const [sub, subMode] of this.s) { | ||
if (subMode === mode) { | ||
invoke(sub, this.c); | ||
} | ||
} | ||
} | ||
}; | ||
var RefValAgent = class { | ||
#agent; | ||
#subs = /* @__PURE__ */ new WeakSet(); | ||
constructor(subs) { | ||
this.#agent = subs; | ||
this.e = subs.e; | ||
this.u = subs.u; | ||
this.n = subs.n; | ||
this.s = subs.s; | ||
} | ||
get v() { | ||
return this.#agent.v; | ||
} | ||
s; | ||
e; | ||
u; | ||
n; | ||
a(subscriber, mode) { | ||
this.#subs.add(subscriber); | ||
return this.#agent.a(subscriber, mode); | ||
} | ||
r(subscriber) { | ||
if (subscriber) { | ||
if (this.#subs.has(subscriber)) { | ||
this.#subs.delete(subscriber); | ||
this.#agent.r(subscriber); | ||
} | ||
} else { | ||
for (const sub of this.#agent.s.keys()) { | ||
this.r(sub); | ||
} | ||
} | ||
} | ||
}; | ||
// src/val.ts | ||
var ValImpl = class { | ||
/** | ||
* Manage subscribers for a val. | ||
*/ | ||
#agent; | ||
constructor(agent) { | ||
this.#agent = agent; | ||
this.get = agent.u; | ||
} | ||
get $version() { | ||
this.get(); | ||
return this.#agent.v; | ||
} | ||
get value() { | ||
return this.get(); | ||
} | ||
set value(value) { | ||
this.set?.(value); | ||
} | ||
set; | ||
get; | ||
ref(writable) { | ||
const val$ = new ValImpl(new RefValAgent(this.#agent)); | ||
return writable && this.set ? attachSetter(val$, this.set) : val$; | ||
} | ||
reaction(subscriber, eager = this.#agent.e) { | ||
return this.#agent.a(subscriber, eager ? 2 /* Eager */ : 1 /* Async */); | ||
} | ||
subscribe(subscriber, eager = this.#agent.e) { | ||
const disposer = this.reaction(subscriber, eager); | ||
invoke(subscriber, this.get()); | ||
return disposer; | ||
} | ||
$valCompute(subscriber) { | ||
return this.#agent.a(subscriber, 3 /* Computed */); | ||
} | ||
unsubscribe(subscriber) { | ||
this.#agent.r(subscriber); | ||
} | ||
dispose() { | ||
this.#agent.r(); | ||
} | ||
/** | ||
* @returns the string representation of `this.value`. | ||
* | ||
* @example | ||
* ```js | ||
* const v$ = val(val(val(1))); | ||
* console.log(`${v$}`); // "1" | ||
* ``` | ||
*/ | ||
toString() { | ||
return String(this.get()); | ||
} | ||
/** | ||
* @returns the JSON representation of `this.value`. | ||
* | ||
* @example | ||
* ```js | ||
* const v$ = val(val(val({ a: 1 }))); | ||
* JSON.stringify(v$); // '{"a":1}' | ||
* ``` | ||
*/ | ||
toJSON(key) { | ||
const value = this.get(); | ||
return value && value.toJSON ? value.toJSON(key) : value; | ||
} | ||
}; | ||
function readonlyVal(value, config) { | ||
let currentValue = value; | ||
const get = () => currentValue; | ||
const subs = new ValAgent(get, config); | ||
const set = (value2) => { | ||
if (!subs.q?.(value2, currentValue)) { | ||
currentValue = value2; | ||
subs.n(); | ||
} | ||
}; | ||
const val2 = new ValImpl(subs); | ||
return [val2, set]; | ||
} | ||
function val(value, config) { | ||
const [val$, set] = readonlyVal(value, config); | ||
return attachSetter(val$, set); | ||
} | ||
var groupVals = (valPairs) => { | ||
const vals = {}; | ||
const setters = {}; | ||
for (const key of Object.keys(valPairs)) { | ||
const [val2, set] = valPairs[key]; | ||
vals[key] = val2; | ||
setters[key] = set; | ||
} | ||
return [vals, setters]; | ||
}; | ||
// src/from.ts | ||
var from = (getValue2, listen, config) => { | ||
return new ValImpl(new ValAgent(getValue2, config, listen)); | ||
}; | ||
// src/combine.ts | ||
function combine(valInputs, transform = identity, config) { | ||
let cachedValue; | ||
let cachedSrcVersions; | ||
return from( | ||
() => { | ||
const versions = valInputs.map(getValVersion); | ||
if (!cachedSrcVersions || !arrayShallowEqual(versions, cachedSrcVersions)) { | ||
cachedSrcVersions = versions; | ||
cachedValue = transform(getValues(valInputs)); | ||
} | ||
return cachedValue; | ||
}, | ||
(notify) => { | ||
const disposers = valInputs.map((val2) => val2.$valCompute(notify)); | ||
return () => disposers.forEach(invoke); | ||
}, | ||
config | ||
); | ||
} | ||
// src/derive.ts | ||
function derive(val2, transform = identity, config) { | ||
let cachedValue; | ||
let cachedSrcVersion = INIT_VALUE; | ||
return from( | ||
() => { | ||
const version = val2.$version; | ||
if (!strictEqual(version, cachedSrcVersion)) { | ||
cachedSrcVersion = version; | ||
cachedValue = transform(val2.value); | ||
} | ||
return cachedValue; | ||
}, | ||
(notify) => val2.$valCompute(notify), | ||
config | ||
); | ||
} | ||
// src/flatten-from.ts | ||
var flattenFrom = (getValue2, listen, config) => { | ||
let innerDisposer; | ||
let currentValVersion = INIT_VALUE; | ||
let currentMaybeVal = INIT_VALUE; | ||
let dirty = true; | ||
const useDefaultEqual = config?.equal == null; | ||
const subs = new ValAgent( | ||
() => { | ||
if (dirty) { | ||
if (subs.s.size) { | ||
dirty = false; | ||
} | ||
const lastMaybeVal = currentMaybeVal; | ||
currentMaybeVal = getValue2(); | ||
if (isVal(currentMaybeVal)) { | ||
if (!strictEqual(currentMaybeVal, lastMaybeVal)) { | ||
innerDisposer &&= innerDisposer(); | ||
if (subs.s.size) { | ||
innerDisposer = currentMaybeVal.$valCompute(subs.n); | ||
} | ||
} | ||
} else { | ||
innerDisposer &&= innerDisposer(); | ||
} | ||
} | ||
if (isVal(currentMaybeVal)) { | ||
const lastValVersion = currentValVersion; | ||
currentValVersion = currentMaybeVal.$version; | ||
if (useDefaultEqual && !strictEqual(currentValVersion, lastValVersion)) { | ||
subs.b |= 12 /* ShouldInvoke */; | ||
} | ||
return currentMaybeVal.value; | ||
} else { | ||
currentValVersion = INIT_VALUE; | ||
return currentMaybeVal; | ||
} | ||
}, | ||
config, | ||
(notify) => { | ||
const outerDisposer = listen(() => { | ||
dirty = true; | ||
notify(); | ||
}); | ||
if (!innerDisposer && isVal(currentMaybeVal)) { | ||
innerDisposer = currentMaybeVal.$valCompute(notify); | ||
} | ||
return () => { | ||
dirty = true; | ||
innerDisposer &&= innerDisposer(); | ||
outerDisposer?.(); | ||
}; | ||
} | ||
); | ||
return new ValImpl(subs); | ||
}; | ||
// src/flatten.ts | ||
function flatten(val2, get = identity, config) { | ||
return flattenFrom( | ||
() => get(val2.value), | ||
(notify) => val2.$valCompute(notify), | ||
config | ||
); | ||
} | ||
// src/index.ts | ||
if (process.env.NODE_ENV !== "production") { | ||
initDev(); | ||
} | ||
exports.arrayShallowEqual = arrayShallowEqual; | ||
exports.attachSetter = attachSetter; | ||
exports.combine = combine; | ||
exports.derive = derive; | ||
exports.flatten = flatten; | ||
exports.flattenFrom = flattenFrom; | ||
exports.from = from; | ||
exports.groupVals = groupVals; | ||
exports.identity = identity; | ||
exports.isVal = isVal; | ||
exports.isWritable = isWritable; | ||
exports.nextTick = nextTick; | ||
exports.reaction = reaction; | ||
exports.readonlyVal = readonlyVal; | ||
exports.setValue = setValue; | ||
exports.strictEqual = strictEqual; | ||
exports.subscribe = subscribe; | ||
exports.unsubscribe = unsubscribe; | ||
exports.val = val; |
{ | ||
"name": "value-enhancer", | ||
"version": "5.1.3", | ||
"version": "5.2.0", | ||
"private": false, | ||
@@ -47,8 +47,10 @@ "description": "A tiny library to enhance value with reactive wrapper.", | ||
"lint": "eslint --ext .ts,.tsx . && prettier --check . && tsc --noEmit", | ||
"test": "tsc --noEmit -p ./test/tsconfig.json && jest", | ||
"test": "jest", | ||
"test:CI": "tsc --noEmit -p ./test/tsconfig.json && jest --collect-coverage", | ||
"docs": "typedoc --options typedoc.json", | ||
"types": "cross-env NODE_ENV=production tsc --declaration --emitDeclarationOnly --jsx react --esModuleInterop --outDir dist", | ||
"build": "cross-env NODE_ENV=production tsup-node", | ||
"build:min": "cross-env NODE_ENV=production MINIFY=true tsup-node && node scripts/gzip.mjs", | ||
"build:dev": "cross-env NODE_ENV=development tsup-node src/index.ts", | ||
"build:index": "tsup --config tsup-config/index.tsup.config.ts", | ||
"build:collections": "tsup --config tsup-config/collections.tsup.config.ts", | ||
"build": "cross-env NODE_ENV=production pnpm run build:index && cross-env NODE_ENV=production pnpm run build:collections", | ||
"build:min": "cross-env NODE_ENV=production MINIFY=true pnpm run build:index && cross-env NODE_ENV=production MINIFY=true pnpm run build:collections && node scripts/gzip.mjs", | ||
"release": "standard-version" | ||
@@ -55,0 +57,0 @@ }, |
@@ -21,3 +21,3 @@ # [value-enhancer](https://github.com/crimx/value-enhancer) | ||
Enhance value with plain and explicit reactive wrapper. | ||
Enhance value with plain and explicit reactive wrapper. Think of it as hook-style signals. | ||
@@ -204,2 +204,11 @@ Docs: <https://value-enhancer.js.org> | ||
## Devtools | ||
<details> | ||
<summary>Custom Formatter</summary> | ||
`value-enhancer` in development mode supports DevTools [custom formatters](https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html). You may enable it by checking the "Enable custom formatters" option in the "Console" section of DevTools general settings. | ||
</details> | ||
## Usage | ||
@@ -206,0 +215,0 @@ |
@@ -1,3 +0,3 @@ | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal, strictEqual } from ".."; | ||
import type { ReadonlyVal } from "value-enhancer"; | ||
import { readonlyVal, strictEqual } from "value-enhancer"; | ||
@@ -4,0 +4,0 @@ /** |
@@ -1,3 +0,3 @@ | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal, strictEqual } from ".."; | ||
import type { ReadonlyVal } from "value-enhancer"; | ||
import { readonlyVal, strictEqual } from "value-enhancer"; | ||
@@ -4,0 +4,0 @@ /** |
@@ -1,3 +0,3 @@ | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal } from ".."; | ||
import type { ReadonlyVal } from "value-enhancer"; | ||
import { readonlyVal } from "value-enhancer"; | ||
@@ -4,0 +4,0 @@ /** |
@@ -1,6 +0,12 @@ | ||
import type { ReadonlyVal, UnwrapVal, ValConfig, ValDisposer } from "./typings"; | ||
import type { | ||
ReadonlyVal, | ||
UnwrapVal, | ||
ValConfig, | ||
ValDisposer, | ||
ValVersion, | ||
} from "./typings"; | ||
import { from } from "./from"; | ||
import { isVal, strictEqual } from "./utils"; | ||
import type { ValImpl } from "./val"; | ||
import { AgentStatus, ValAgent } from "./agent"; | ||
import { INIT_VALUE, isVal, strictEqual } from "./utils"; | ||
import { ValImpl } from "./val"; | ||
@@ -41,62 +47,63 @@ /** | ||
): ReadonlyVal<UnwrapVal<TValOrValue>> => { | ||
const initialEqual = config?.equal; | ||
let innerMaybeVal: TValOrValue | undefined; | ||
let innerVal: ValImpl<UnwrapVal<TValOrValue>> | undefined | null; | ||
let innerDisposer: ValDisposer | undefined | null; | ||
let withSubscriber = false; | ||
let innerDisposer: ValDisposer | undefined | void; | ||
let currentValVersion: ValVersion = INIT_VALUE; | ||
let currentMaybeVal: TValOrValue = INIT_VALUE; | ||
let dirty = true; | ||
const useDefaultEqual = config?.equal == null; | ||
const computeValue = (): UnwrapVal<TValOrValue> => { | ||
if (!withSubscriber) { | ||
updateInnerVal(); | ||
} | ||
return innerVal | ||
? innerVal.value | ||
: (innerMaybeVal as UnwrapVal<TValOrValue>); | ||
}; | ||
const subs = new ValAgent( | ||
() => { | ||
if (dirty) { | ||
if (subs.subs_.size) { | ||
dirty = false; | ||
} | ||
const updateInnerVal = () => { | ||
const maybeVal = getValue(); | ||
if (!strictEqual(maybeVal, innerMaybeVal)) { | ||
innerMaybeVal = maybeVal; | ||
innerVal = isVal(maybeVal) | ||
? (maybeVal as unknown as ValImpl<UnwrapVal<TValOrValue>>) | ||
: null; | ||
(val$ as ValImpl).$equal = | ||
initialEqual || | ||
(initialEqual === false | ||
? void 0 | ||
: innerVal | ||
? innerVal.$equal | ||
: strictEqual); | ||
} | ||
}; | ||
const lastMaybeVal = currentMaybeVal; | ||
currentMaybeVal = getValue(); | ||
const updateInnerValCompute = (notify: () => void) => { | ||
innerDisposer?.(); | ||
innerDisposer = innerVal && innerVal.$valCompute(notify); | ||
}; | ||
if (isVal(currentMaybeVal)) { | ||
if (!strictEqual(currentMaybeVal, lastMaybeVal)) { | ||
innerDisposer &&= innerDisposer(); | ||
if (subs.subs_.size) { | ||
innerDisposer = currentMaybeVal.$valCompute(subs.notify_); | ||
} | ||
} | ||
} else { | ||
innerDisposer &&= innerDisposer(); | ||
} | ||
} | ||
const val$ = from( | ||
computeValue, | ||
if (isVal(currentMaybeVal)) { | ||
const lastValVersion = currentValVersion; | ||
currentValVersion = currentMaybeVal.$version; | ||
if ( | ||
useDefaultEqual && | ||
!strictEqual(currentValVersion, lastValVersion) | ||
) { | ||
subs.status_ |= AgentStatus.ShouldInvoke; | ||
} | ||
return currentMaybeVal.value; | ||
} else { | ||
currentValVersion = INIT_VALUE; | ||
return currentMaybeVal; | ||
} | ||
}, | ||
config, | ||
notify => { | ||
withSubscriber = true; | ||
updateInnerVal(); | ||
updateInnerValCompute(notify); | ||
const outerDisposer = listen(() => { | ||
updateInnerVal(); | ||
updateInnerValCompute(notify); | ||
dirty = true; | ||
notify(); | ||
}); | ||
if (!innerDisposer && isVal(currentMaybeVal)) { | ||
innerDisposer = currentMaybeVal.$valCompute(notify); | ||
} | ||
return () => { | ||
withSubscriber = false; | ||
innerDisposer?.(); | ||
dirty = true; | ||
innerDisposer &&= innerDisposer(); | ||
outerDisposer?.(); | ||
}; | ||
}, | ||
config | ||
} | ||
); | ||
return val$; | ||
return new ValImpl(subs); | ||
}; |
@@ -1,64 +0,5 @@ | ||
import { Subscribers } from "./subscribers"; | ||
import type { | ||
ReadonlyVal, | ||
ValConfig, | ||
ValDisposer, | ||
ValVersion, | ||
} from "./typings"; | ||
import { INIT_VALUE } from "./utils"; | ||
import { ValAgent } from "./agent"; | ||
import type { ReadonlyVal, ValConfig, ValDisposer } from "./typings"; | ||
import { ValImpl } from "./val"; | ||
class FromImpl<TValue = any> extends ValImpl<TValue> { | ||
public constructor( | ||
getValue: () => TValue, | ||
listen: (notify: () => void) => ValDisposer | void | undefined, | ||
config?: ValConfig<TValue> | ||
) { | ||
let currentValue = INIT_VALUE as TValue; | ||
let dirty = false; | ||
let notified = false; | ||
const get = () => { | ||
if (currentValue === INIT_VALUE || subs.size_ <= 0) { | ||
currentValue = getValue(); | ||
subs.newVersion_(config, currentValue); | ||
} else if (dirty) { | ||
const value = getValue(); | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config, value, currentValue); | ||
currentValue = value; | ||
} | ||
} | ||
dirty = notified = false; | ||
return currentValue; | ||
}; | ||
const notify = () => { | ||
dirty = true; | ||
if (!notified) { | ||
notified = true; | ||
subs.notify_(); | ||
} | ||
}; | ||
const subs = new Subscribers(get, subs => { | ||
// attach listener first so that upstream value is resolved | ||
const disposer = listen(notify); | ||
currentValue = getValue(); | ||
subs.newVersion_(config, currentValue); | ||
dirty = notified = false; | ||
return disposer; | ||
}); | ||
super(subs, config); | ||
} | ||
override get $version(): ValVersion { | ||
// resolve current value for the latest version | ||
this.get(); | ||
return super.$version; | ||
} | ||
} | ||
/** | ||
@@ -98,2 +39,4 @@ * Creates a readonly val from a getter function and a listener function. | ||
config?: ValConfig<TValue> | ||
): ReadonlyVal<TValue> => new FromImpl(getValue, listen, config); | ||
): ReadonlyVal<TValue> => { | ||
return new ValImpl(new ValAgent(getValue, config, listen)); | ||
}; |
@@ -0,1 +1,3 @@ | ||
import { initDev } from "./dev"; | ||
export { combine, type CombineValTransform } from "./combine"; | ||
@@ -24,2 +26,3 @@ export { derive, type DerivedValTransform } from "./derive"; | ||
isVal, | ||
isWritable, | ||
reaction, | ||
@@ -32,1 +35,5 @@ setValue, | ||
export { groupVals, readonlyVal, val } from "./val"; | ||
if (process.env.NODE_ENV !== "production") { | ||
initDev(); | ||
} |
@@ -1,32 +0,30 @@ | ||
import type { Subscribers } from "./subscribers"; | ||
import { SubscriberMode } from "./subscribers"; | ||
export interface Task { | ||
runTask_(): void; | ||
} | ||
export type Task<TValue = any> = (value: TValue) => void; | ||
const tick = /*#__PURE__*/ Promise.resolve(); | ||
const pendingSubs1 = new Set<Subscribers>(); | ||
const pendingSubs2 = new Set<Subscribers>(); | ||
let pendingSubs = pendingSubs1; | ||
const pendingTasks1 = new Set<Task>(); | ||
const pendingTasks2 = new Set<Task>(); | ||
let pendingTasks = pendingTasks1; | ||
let pending: Promise<void> | false; | ||
const flush = () => { | ||
const curPendingSubs = pendingSubs; | ||
pendingSubs = pendingSubs === pendingSubs1 ? pendingSubs2 : pendingSubs1; | ||
const currentPendingTasks = pendingTasks; | ||
pendingTasks = pendingTasks === pendingTasks1 ? pendingTasks2 : pendingTasks1; | ||
pending = false; | ||
for (const subs of curPendingSubs) { | ||
subs.exec_(SubscriberMode.Async); | ||
for (const subs of currentPendingTasks) { | ||
subs.runTask_(); | ||
} | ||
curPendingSubs.clear(); | ||
currentPendingTasks.clear(); | ||
}; | ||
export const schedule = <TValue>(subs: Subscribers<TValue>): void => { | ||
pendingSubs.add(subs); | ||
export const schedule = (task: Task): void => { | ||
pendingTasks.add(task); | ||
pending = pending || tick.then(flush); | ||
}; | ||
export const cancelTask = (subs: Subscribers): boolean => | ||
pendingSubs.delete(subs); | ||
export const cancelTask = (task: Task): boolean => pendingTasks.delete(task); | ||
export const nextTick = (): Promise<void> => tick; |
@@ -17,3 +17,3 @@ /** | ||
*/ | ||
get(this: void): TValue; | ||
get: () => TValue; | ||
/** | ||
@@ -69,3 +69,3 @@ * Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. | ||
/** Set new value */ | ||
set: (this: void, value: TValue) => void; | ||
set: (value: TValue) => void; | ||
/** | ||
@@ -72,0 +72,0 @@ * Create a new ReadonlyVal referencing the value of the current ReadonlyVal as source. |
@@ -10,4 +10,7 @@ import type { | ||
export const INIT_VALUE: any = {}; | ||
/** | ||
* @deprecated | ||
* @ignore | ||
* Set the value of a val. | ||
@@ -20,3 +23,3 @@ * It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`). | ||
value: TValue | ||
): void => (val as Val<TValue>).set?.(value); | ||
): void => (val as Val<TValue>)?.set?.(value); | ||
@@ -127,3 +130,6 @@ /** | ||
export const INIT_VALUE: any = {}; | ||
interface IsVal { | ||
<T extends ReadonlyVal>(val$: T): val$ is T extends ReadonlyVal ? T : never; | ||
(val$: unknown): val$ is ReadonlyVal; | ||
} | ||
@@ -135,13 +141,11 @@ /** | ||
*/ | ||
export function isVal<T extends ReadonlyVal>( | ||
val: T | ||
): val is T extends ReadonlyVal ? T : never; | ||
export const isVal: IsVal = (val$: unknown): val$ is ReadonlyVal => | ||
!!(val$ as any)?.$valCompute; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
* Checks if `val` is a writable `Val`. | ||
* @returns `true` if `val` is a writable `Val`. | ||
*/ | ||
export function isVal(val: unknown): val is ReadonlyVal; | ||
export function isVal(val: unknown): val is ReadonlyVal { | ||
return !!(val && (val as any).$valCompute); | ||
} | ||
export const isWritable = <TValue>( | ||
val$: ReadonlyVal<TValue> | ||
): val$ is Val<TValue> => !!(val$ as Val)?.set; |
@@ -12,4 +12,5 @@ import type { | ||
import { SubscriberMode, Subscribers } from "./subscribers"; | ||
import { attachSetter, invoke, strictEqual } from "./utils"; | ||
import type { IValAgent } from "./agent"; | ||
import { RefValAgent, SubMode, ValAgent } from "./agent"; | ||
import { attachSetter, invoke } from "./utils"; | ||
@@ -24,24 +25,13 @@ /** | ||
*/ | ||
readonly #subs: Subscribers<TValue>; | ||
readonly #agent: IValAgent<TValue>; | ||
readonly #config?: ValConfig; | ||
readonly #eager?: boolean; | ||
/** | ||
* @param get A pure function that returns the current value of the val. | ||
* @param config Custom config for the val. | ||
* @param start A function that is called when a val get its first subscriber. | ||
* The returned disposer will be called when the last subscriber unsubscribed from the val. | ||
*/ | ||
public constructor(subs: Subscribers<TValue>, config?: ValConfig<TValue>) { | ||
this.#subs = subs; | ||
this.get = subs.getValue_; | ||
this.#config = config; | ||
this.$equal = (config?.equal ?? strictEqual) || void 0; | ||
this.#eager = config?.eager; | ||
public constructor(agent: IValAgent<TValue>) { | ||
this.#agent = agent; | ||
this.get = agent.resolveValue_; | ||
} | ||
public get $version(): ValVersion { | ||
return this.#subs.version_; | ||
// resolve current value for the latest version | ||
this.get(); | ||
return this.#agent.version_; | ||
} | ||
@@ -54,16 +44,12 @@ | ||
public set value(value: TValue) { | ||
this.set(value); | ||
this.set?.(value); | ||
} | ||
public set(this: void, _value: TValue): void { | ||
// do nothing | ||
} | ||
public set?: ValSetValue<TValue>; | ||
public get: (this: void) => TValue; | ||
public $equal?: (this: void, newValue: TValue, oldValue: TValue) => boolean; | ||
public ref(writable?: boolean): ReadonlyVal<TValue> { | ||
const val$ = new ValRefImpl(this, this.#config); | ||
return writable ? attachSetter(val$, this.set) : val$; | ||
const val$ = new ValImpl(new RefValAgent(this.#agent)); | ||
return writable && this.set ? attachSetter(val$, this.set) : val$; | ||
} | ||
@@ -73,8 +59,5 @@ | ||
subscriber: ValSubscriber<TValue>, | ||
eager = this.#eager | ||
eager = this.#agent.eager_ | ||
): ValDisposer { | ||
return this.#subs.add_( | ||
subscriber, | ||
eager ? SubscriberMode.Eager : SubscriberMode.Async | ||
); | ||
return this.#agent.add_(subscriber, eager ? SubMode.Eager : SubMode.Async); | ||
} | ||
@@ -84,7 +67,6 @@ | ||
subscriber: ValSubscriber<TValue>, | ||
eager = this.#eager | ||
eager = this.#agent.eager_ | ||
): ValDisposer { | ||
const disposer = this.reaction(subscriber, eager); | ||
invoke(subscriber, this.value); | ||
this.#subs.dirty_ = false; | ||
invoke(subscriber, this.get()); | ||
return disposer; | ||
@@ -94,15 +76,11 @@ } | ||
public $valCompute(subscriber: ValSubscriber<void>): ValDisposer { | ||
return this.#subs.add_(subscriber, SubscriberMode.Computed); | ||
return this.#agent.add_(subscriber, SubMode.Computed); | ||
} | ||
public unsubscribe(subscriber?: (...args: any[]) => any): void { | ||
if (subscriber) { | ||
this.#subs.remove_(subscriber); | ||
} else { | ||
this.#subs.clear_(); | ||
} | ||
this.#agent.remove_(subscriber); | ||
} | ||
public dispose(): void { | ||
this.#subs.clear_(); | ||
this.#agent.remove_(); | ||
} | ||
@@ -120,3 +98,3 @@ | ||
public toString(): string { | ||
return String(this.value); | ||
return String(this.get()); | ||
} | ||
@@ -134,3 +112,3 @@ | ||
public toJSON(key: string): unknown { | ||
const value = this.value as | ||
const value = this.get() as | ||
| undefined | ||
@@ -143,15 +121,2 @@ | null | ||
export class ValRefImpl<TValue = any> extends ValImpl<TValue> { | ||
public constructor(source$: ValImpl<TValue>, config?: ValConfig<TValue>) { | ||
const subs = new Subscribers(source$.get, () => | ||
source$.$valCompute(() => { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config); | ||
subs.notify_(); | ||
}) | ||
); | ||
super(subs, config); | ||
} | ||
} | ||
/** | ||
@@ -213,8 +178,6 @@ * Creates a readonly val with the given value. | ||
const subs = new Subscribers(get); | ||
const subs = new ValAgent(get, config); | ||
const set = (value: TValue | undefined): void => { | ||
if (!val.$equal?.(value, currentValue)) { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config, value, currentValue); | ||
if (!subs.equal_?.(value, currentValue)) { | ||
currentValue = value; | ||
@@ -225,3 +188,3 @@ subs.notify_(); | ||
const val = new ValImpl(subs, config); | ||
const val = new ValImpl(subs); | ||
@@ -228,0 +191,0 @@ return [val, set]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
156143
4526
622
27