Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

value-enhancer

Package Overview
Dependencies
Maintainers
1
Versions
87
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

value-enhancer - npm Package Compare versions

Comparing version 5.1.3 to 5.2.0

src/agent.ts

63

dist/collections.d.ts

@@ -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 @@ /**

18

dist/collections.js
'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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc