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.2 to 5.1.3

dist/chunk-JNRMXROO.js

2

dist/collections.d.ts

@@ -1,2 +0,2 @@

import { R as ReadonlyVal } from './typings-2bae36c2.js';
import { R as ReadonlyVal } from './typings-e10eb4db.js';

@@ -3,0 +3,0 @@ /**

'use strict';
var chunkLU5CXO64_js = require('./chunk-LU5CXO64.js');
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js');

@@ -8,3 +8,3 @@ // src/collections/list.ts

constructor(items) {
const [$, set$] = chunkLU5CXO64_js.readonlyVal(
const [$, set$] = chunkJNRMXROO_js.readonlyVal(
items ? [...items] : [],

@@ -92,3 +92,3 @@ { equal: false }

set(index, item) {
if (index >= 0 && !chunkLU5CXO64_js.strictEqual(this.array[index], item)) {
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) {
this.array[index] = item;

@@ -102,3 +102,3 @@ this.#notify();

for (const [index, item] of entries) {
if (index >= 0 && !chunkLU5CXO64_js.strictEqual(this.array[index], item)) {
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) {
isDirty = true;

@@ -183,3 +183,3 @@ this.array[index] = item;

}
const [$, set$] = chunkLU5CXO64_js.readonlyVal(this, { equal: false });
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false });
this.$ = $;

@@ -242,3 +242,3 @@ this.#notify = () => set$(this);

const oldValue = this.get(key);
if (chunkLU5CXO64_js.strictEqual(oldValue, value)) {
if (chunkJNRMXROO_js.strictEqual(oldValue, value)) {
return false;

@@ -316,3 +316,3 @@ }

}
const [$, set$] = chunkLU5CXO64_js.readonlyVal(this, { equal: false });
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false });
this.$ = $;

@@ -319,0 +319,0 @@ this.#notify = () => set$(this);

@@ -1,35 +0,4 @@

import { R as ReadonlyVal, V as ValInputsValueTuple, a as ValConfig, U as UnwrapVal, b as ValDisposer, N as NoInfer, c as ValSetValue, d as Val, e as ValSubscriber } from './typings-2bae36c2.js';
export { F as FlattenVal, f as ValEqual, g as ValVersion } from './typings-2bae36c2.js';
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';
declare const nextTick: () => Promise<void>;
/** Returns the value passed in. */
declare const identity: <TValue>(value: TValue) => TValue;
/**
* `Object.is`
*/
declare const strictEqual: (value1: any, value2: any) => boolean;
/**
* Shallow compare two arrays.
* @param arrA - any value
* @param arrB - any value
* @returns `false` if any of:
* 1. one of arrA or arrB is an array and the other is not
* 2. arrA and arrB have different lengths
* 3. arrA and arrB have different values at any index
*/
declare const arrayShallowEqual: (arrA: any, arrB: any) => boolean;
/**
* Checks if `val` is `ReadonlyVal` or `Val`.
*
* @returns `true` if `val` is `ReadonlyVal` or `Val`.
*/
declare function isVal<T extends ReadonlyVal>(val: T): val is T extends ReadonlyVal ? T : never;
/**
* Checks if `val` is `ReadonlyVal` or `Val`.
*
* @returns `true` if `val` is `ReadonlyVal` or `Val`.
*/
declare function isVal(val: unknown): val is ReadonlyVal;
type CombineValTransform<TCombinedValue = any, TValues extends readonly any[] = any[]> = (newValues: TValues) => TCombinedValue;

@@ -116,2 +85,13 @@ /**

*/
/**
* Creates a readonly val from a getter function and a listener function.
* If the value is a val, it will be auto-flattened.
*
* @param getValue A function that returns the current value.
* If the value is a val, it will be auto-flattened.
* @param listen A function that takes a notify function and returns a disposer.
* The notify function should be called when the value changes.
* @param config custom config for the val.
* @returns A readonly val with value of inner val.
*/
declare const flattenFrom: <TValOrValue = any>(getValue: () => TValOrValue, listen: (notify: () => void) => ValDisposer | void | undefined, config?: ValConfig<UnwrapVal<TValOrValue>> | undefined) => ReadonlyVal<UnwrapVal<TValOrValue>>;

@@ -151,3 +131,71 @@

declare const nextTick: () => Promise<void>;
/**
* @deprecated
* Set the value of a val.
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`).
* Do nothing if the val is really `ReadonlyVal`.
*/
declare const setValue: <TValue>(val: ReadonlyVal<TValue>, value: TValue) => void;
/**
* Subscribe to value changes with immediate emission.
* @param val
* @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
*/
declare const subscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer;
/**
* Subscribe to value changes without immediate emission.
* @param val
* @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
*/
declare const reaction: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer;
/**
* Remove the given subscriber.
* Remove all if no subscriber provided.
* @param val
* @param subscriber
*/
declare const unsubscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber?: ((...args: any[]) => any) | undefined) => void;
/** Returns the value passed in. */
declare const identity: <TValue>(value: TValue) => TValue;
/**
* `Object.is`
*/
declare const strictEqual: (value1: any, value2: any) => boolean;
/**
* Shallow compare two arrays.
* @param arrA - any value
* @param arrB - any value
* @returns `false` if any of:
* 1. one of arrA or arrB is an array and the other is not
* 2. arrA and arrB have different lengths
* 3. arrA and arrB have different values at any index
*/
declare const arrayShallowEqual: (arrA: any, arrB: any) => boolean;
/**
* Attach a new setter to a val.
* @param val$ a readonly Val
* @param set a function that sets the value of val$
* @returns The same val$ with the new setter.
*/
declare const attachSetter: <TValue>(val$: ReadonlyVal<TValue>, set: (this: void, value: TValue) => void) => Val<TValue>;
/**
* Checks if `val` is `ReadonlyVal` or `Val`.
*
* @returns `true` if `val` is `ReadonlyVal` or `Val`.
*/
declare function isVal<T extends ReadonlyVal>(val: T): val is T extends ReadonlyVal ? T : never;
/**
* Checks if `val` is `ReadonlyVal` or `Val`.
*
* @returns `true` if `val` is `ReadonlyVal` or `Val`.
*/
declare function isVal(val: unknown): val is ReadonlyVal;
/**
* Creates a readonly val with the given value.

@@ -164,2 +212,4 @@ *

*
* @param value Value for the val
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.

@@ -172,3 +222,3 @@ */

* @param value Value for the val
* @param config Custom config for the val.
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.

@@ -178,2 +228,36 @@ */

/**
* Creates a readonly val with the given value.
*
* @param value Optional value for the val
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.
*/
declare function readonlyVal<TValue = any>(value?: TValue, config?: ValConfig<TValue>): [
ReadonlyVal<NoInfer<TValue | undefined>>,
ValSetValue<NoInfer<TValue | undefined>>
];
/**
* Creates a writable val.
* @returns A val with undefined value.
*/
declare function val<TValue = any>(): Val<NoInfer<TValue> | undefined>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Optional custom config.
*/
declare function val(value: [], config?: ValConfig<any[]>): Val<any[]>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Optional custom config.
*/
declare function val<TValue = any>(value: TValue, config?: ValConfig<TValue>): Val<NoInfer<TValue>>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Optional custom config.
*/
declare function val<TValue = any>(value?: TValue, config?: ValConfig<TValue | undefined>): Val<NoInfer<TValue>>;
/**
* Takes an object of key-value pairs containing `ReadonlyVal` instances and their corresponding `ValSetValue` functions,

@@ -222,50 +306,2 @@ * and returns a tuple containing an array of the `ReadonlyVal` instances and a function to set their values.

/**
* Creates a writable val.
* @returns A val with undefined value.
*/
declare function val<TValue = any>(): Val<NoInfer<TValue> | undefined>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Custom config.
*/
declare function val(value: [], config?: ValConfig<any[]>): Val<any[]>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Custom config.
*/
declare function val<TValue = any>(value: TValue, config?: ValConfig<TValue>): Val<NoInfer<TValue>>;
/**
* Set the value of a val.
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`).
* Does nothing if the val is really `ReadonlyVal`.
*/
declare const setValue: <TValue>(val: ReadonlyVal<TValue>, value: TValue) => void;
/**
* Subscribe to value changes with immediate emission.
* @param val
* @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
*/
declare const subscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer;
/**
* Subscribe to value changes without immediate emission.
* @param val
* @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
*/
declare const reaction: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer;
/**
* Remove the given subscriber.
* Remove all if no subscriber provided.
* @param val
* @param subscriber
*/
declare const unsubscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber?: ((...args: any[]) => any) | undefined) => void;
export { CombineValTransform, DerivedValTransform, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, arrayShallowEqual, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, nextTick, reaction, readonlyVal, setValue, strictEqual, subscribe, unsubscribe, val };
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 };
'use strict';
var chunkLU5CXO64_js = require('./chunk-LU5CXO64.js');
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js');
// src/from.ts
var FromImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl {
constructor(getValue, listen, config) {
let currentValue = chunkLU5CXO64_js.INIT_VALUE;
let dirty = false;
let notified = false;
const get = () => {
if (currentValue === chunkLU5CXO64_js.INIT_VALUE || subs.s <= 0) {
currentValue = getValue();
subs.w(config, currentValue);
} else if (dirty) {
const value = getValue();
if (!this.$equal?.(value, currentValue)) {
subs.d = true;
subs.w(config, value, currentValue);
currentValue = value;
}
}
dirty = notified = false;
return currentValue;
};
const notify = () => {
dirty = true;
if (!notified) {
notified = true;
subs.n();
}
};
const subs = new chunkLU5CXO64_js.Subscribers(get, (subs2) => {
const disposer = listen(notify);
currentValue = getValue();
subs2.w(config, currentValue);
dirty = notified = false;
return disposer;
});
super(subs, config);
}
get $version() {
this.get();
return super.$version;
}
};
var from = (getValue, listen, config) => new FromImpl(getValue, listen, config);
// src/combine.ts
function combine(valInputs, transform = chunkLU5CXO64_js.identity, config) {
let cachedValue;
let cachedSrcVersions;
return from(
() => {
const versions = valInputs.map(chunkLU5CXO64_js.getValVersion);
if (!cachedSrcVersions || !chunkLU5CXO64_js.arrayShallowEqual(versions, cachedSrcVersions)) {
cachedSrcVersions = versions;
cachedValue = transform(chunkLU5CXO64_js.getValues(valInputs));
}
return cachedValue;
},
(notify) => {
const disposers = valInputs.map((val2) => val2.$valCompute(notify));
return () => disposers.forEach(chunkLU5CXO64_js.invoke);
},
config
);
}
// src/derive.ts
function derive(val2, transform = chunkLU5CXO64_js.identity, config) {
let cachedValue;
let cachedSrcVersion = chunkLU5CXO64_js.INIT_VALUE;
return from(
() => {
const version = val2.$version;
if (!chunkLU5CXO64_js.strictEqual(version, cachedSrcVersion)) {
cachedSrcVersion = version;
cachedValue = transform(val2.value);
}
return cachedValue;
},
(notify) => val2.$valCompute(notify),
config
);
}
// src/flatten-from.ts
var FlattenFromImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl {
constructor(getValue, listen, config) {
const initialEqual = config?.equal;
let currentValue = chunkLU5CXO64_js.INIT_VALUE;
let dirty = false;
let notified = false;
let innerMaybeVal;
let innerVal;
let innerDisposer;
const computeValue = () => {
if (subs.s <= 0) {
updateInnerVal();
}
return innerVal ? innerVal.value : innerMaybeVal;
};
const get = () => {
if (currentValue === chunkLU5CXO64_js.INIT_VALUE || subs.s <= 0) {
currentValue = computeValue();
subs.w(config, currentValue);
} else if (dirty) {
const value = computeValue();
if (!this.$equal?.(value, currentValue)) {
subs.d = true;
subs.w(config, value, currentValue);
currentValue = value;
}
}
dirty = notified = false;
return currentValue;
};
const updateInnerVal = () => {
const maybeVal = getValue();
if (!chunkLU5CXO64_js.strictEqual(maybeVal, innerMaybeVal)) {
innerMaybeVal = maybeVal;
innerVal = chunkLU5CXO64_js.isVal(maybeVal) ? maybeVal : null;
innerDisposer?.();
innerDisposer = innerVal && innerVal.$valCompute(notify);
this.$equal = initialEqual || (initialEqual === false ? void 0 : innerVal ? innerVal.$equal : chunkLU5CXO64_js.strictEqual);
}
};
const notify = () => {
dirty = true;
if (!notified) {
notified = true;
subs.n();
}
};
const subs = new chunkLU5CXO64_js.Subscribers(get, (subs2) => {
const outerDisposer = listen(() => {
updateInnerVal();
notify();
});
updateInnerVal();
currentValue = innerVal ? innerVal.value : innerMaybeVal;
subs2.w(config, currentValue);
dirty = notified = false;
return () => {
innerDisposer?.();
outerDisposer?.();
};
});
super(subs, config);
}
get $version() {
this.get();
return super.$version;
}
};
var flattenFrom = (getValue, listen, config) => new FlattenFromImpl(getValue, listen, config);
// src/flatten.ts
function flatten(val2, get = chunkLU5CXO64_js.identity, config) {
return flattenFrom(
() => get(val2.value),
(notify) => val2.$valCompute(notify),
config
);
}
// src/val.ts
var ValImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl {
#config;
constructor(currentValue, config) {
const get = () => currentValue;
const subs = new chunkLU5CXO64_js.Subscribers(get);
super(subs, config);
this.#config = config;
this.set = (value) => {
if (!this.$equal?.(value, currentValue)) {
subs.d = true;
subs.w(config, value, currentValue);
currentValue = value;
subs.n();
}
};
}
set;
get value() {
return this.get();
}
set value(value) {
this.set(value);
}
ref(writable) {
return writable ? new ValRefImpl(this, this.#config) : new chunkLU5CXO64_js.ReadonlyValRefImpl(this, this.#config);
}
};
var ValRefImpl = class extends chunkLU5CXO64_js.ReadonlyValRefImpl {
#source$;
#config;
constructor(source$, config) {
super(source$);
this.#source$ = source$;
this.#config = config;
this.set = source$.set;
}
set;
get value() {
return this.get();
}
set value(value) {
this.set(value);
}
ref(writable) {
return writable ? new ValRefImpl(this.#source$, this.#config) : new chunkLU5CXO64_js.ReadonlyValRefImpl(this.#source$, this.#config);
}
};
function val(value, config) {
return new ValImpl(value, config);
}
// src/value-enhancer.ts
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);
Object.defineProperty(exports, 'arrayShallowEqual', {
enumerable: true,
get: function () { return chunkLU5CXO64_js.arrayShallowEqual; }
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 chunkLU5CXO64_js.groupVals; }
get: function () { return chunkJNRMXROO_js.groupVals; }
});
Object.defineProperty(exports, 'identity', {
enumerable: true,
get: function () { return chunkLU5CXO64_js.identity; }
get: function () { return chunkJNRMXROO_js.identity; }
});
Object.defineProperty(exports, 'isVal', {
enumerable: true,
get: function () { return chunkLU5CXO64_js.isVal; }
get: function () { return chunkJNRMXROO_js.isVal; }
});
Object.defineProperty(exports, 'nextTick', {
enumerable: true,
get: function () { return chunkLU5CXO64_js.nextTick; }
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 chunkLU5CXO64_js.readonlyVal; }
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 chunkLU5CXO64_js.strictEqual; }
get: function () { return chunkJNRMXROO_js.strictEqual; }
});
exports.combine = combine;
exports.derive = derive;
exports.flatten = flatten;
exports.flattenFrom = flattenFrom;
exports.from = from;
exports.reaction = reaction;
exports.setValue = setValue;
exports.subscribe = subscribe;
exports.unsubscribe = unsubscribe;
exports.val = val;
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; }
});
{
"name": "value-enhancer",
"version": "5.1.2",
"version": "5.1.3",
"private": false,

@@ -44,2 +44,13 @@ "description": "A tiny library to enhance value with reactive wrapper.",

],
"scripts": {
"prepublishOnly": "pnpm run build",
"lint": "eslint --ext .ts,.tsx . && prettier --check . && tsc --noEmit",
"test": "tsc --noEmit -p ./test/tsconfig.json && jest",
"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",
"release": "standard-version"
},
"devDependencies": {

@@ -63,13 +74,3 @@ "@jest/globals": "^29.5.0",

"yoctocolors": "^1.0.0"
},
"scripts": {
"lint": "eslint --ext .ts,.tsx . && prettier --check . && tsc --noEmit",
"test": "tsc --noEmit -p ./test/tsconfig.json && jest",
"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",
"release": "standard-version"
}
}
}

@@ -1,4 +0,3 @@

import { readonlyVal } from "../readonly-val";
import type { ReadonlyVal } from "../typings";
import { strictEqual } from "../utils";
import type { ReadonlyVal } from "..";
import { readonlyVal, strictEqual } from "..";

@@ -5,0 +4,0 @@ /**

@@ -1,4 +0,3 @@

import { readonlyVal } from "../readonly-val";
import type { ReadonlyVal } from "../typings";
import { strictEqual } from "../utils";
import type { ReadonlyVal } from "..";
import { readonlyVal, strictEqual } from "..";

@@ -5,0 +4,0 @@ /**

@@ -1,3 +0,3 @@

import { readonlyVal } from "../readonly-val";
import type { ReadonlyVal } from "../typings";
import type { ReadonlyVal } from "..";
import { readonlyVal } from "..";

@@ -4,0 +4,0 @@ /**

@@ -1,126 +0,101 @@

import type {
ReadonlyVal,
UnwrapVal,
ValConfig,
ValDisposer,
ValVersion,
} from "./typings";
import type { ReadonlyVal, UnwrapVal, ValConfig, ValDisposer } from "./typings";
import { ReadonlyValImpl } from "./readonly-val";
import { Subscribers } from "./subscribers";
import { INIT_VALUE, isVal, strictEqual } from "./utils";
import { from } from "./from";
import { isVal, strictEqual } from "./utils";
import type { ValImpl } from "./val";
class FlattenFromImpl<
TValOrValue = any,
TValue = UnwrapVal<TValOrValue>
> extends ReadonlyValImpl<TValue> {
public constructor(
getValue: () => TValOrValue,
listen: (handler: () => void) => ValDisposer | void | undefined,
config?: ValConfig<TValue>
) {
const initialEqual = config?.equal;
/**
* Creates a readonly val from a getter function and a listener function.
* If the value is a val, it will be auto-flattened.
*
* @param getValue A function that returns the current value.
* If the value is a val, it will be auto-flattened.
* @param listen A function that takes a notify function and returns a disposer.
* The notify function should be called when the value changes.
* @param config custom config for the val.
* @returns A readonly val with value of inner val.
*/
// export const flattenFrom2 = <TValOrValue = any>(
// getValue: () => TValOrValue,
// listen: (notify: () => void) => ValDisposer | void | undefined,
// config?: ValConfig<UnwrapVal<TValOrValue>>
// ): ReadonlyVal<UnwrapVal<TValOrValue>> =>
// new FlattenFromImpl(getValue, listen, config);
let currentValue = INIT_VALUE as TValue;
let dirty = false;
let notified = false;
/**
* Creates a readonly val from a getter function and a listener function.
* If the value is a val, it will be auto-flattened.
*
* @param getValue A function that returns the current value.
* If the value is a val, it will be auto-flattened.
* @param listen A function that takes a notify function and returns a disposer.
* The notify function should be called when the value changes.
* @param config custom config for the val.
* @returns A readonly val with value of inner val.
*/
export const flattenFrom = <TValOrValue = any>(
getValue: () => TValOrValue,
listen: (notify: () => void) => ValDisposer | void | undefined,
config?: ValConfig<UnwrapVal<TValOrValue>>
): 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 innerMaybeVal: TValOrValue | undefined;
let innerVal: ReadonlyValImpl<TValue> | undefined | null;
let innerDisposer: ValDisposer | undefined | null;
const computeValue = (): UnwrapVal<TValOrValue> => {
if (!withSubscriber) {
updateInnerVal();
}
return innerVal
? innerVal.value
: (innerMaybeVal as UnwrapVal<TValOrValue>);
};
const computeValue = (): TValue => {
if (subs.size_ <= 0) {
updateInnerVal();
}
return innerVal ? innerVal.value : (innerMaybeVal as TValue);
};
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 get = () => {
if (currentValue === INIT_VALUE || subs.size_ <= 0) {
currentValue = computeValue();
subs.newVersion_(config, currentValue);
} else if (dirty) {
const value = computeValue();
if (!this.$equal?.(value, currentValue)) {
subs.dirty_ = true;
subs.newVersion_(config, value, currentValue);
currentValue = value;
}
}
dirty = notified = false;
return currentValue;
};
const updateInnerValCompute = (notify: () => void) => {
innerDisposer?.();
innerDisposer = innerVal && innerVal.$valCompute(notify);
};
const updateInnerVal = () => {
const maybeVal = getValue();
if (!strictEqual(maybeVal, innerMaybeVal)) {
innerMaybeVal = maybeVal;
innerVal = isVal(maybeVal)
? (maybeVal as unknown as ReadonlyValImpl<TValue>)
: null;
innerDisposer?.();
innerDisposer = innerVal && innerVal.$valCompute(notify);
this.$equal =
initialEqual ||
(initialEqual === false
? void 0
: innerVal
? innerVal.$equal
: strictEqual);
}
};
const val$ = from(
computeValue,
notify => {
withSubscriber = true;
updateInnerVal();
updateInnerValCompute(notify);
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 outerDisposer = listen(() => {
updateInnerVal();
updateInnerValCompute(notify);
notify();
});
updateInnerVal();
currentValue = innerVal ? innerVal.value : (innerMaybeVal as TValue);
subs.newVersion_(config, currentValue);
dirty = notified = false;
return () => {
withSubscriber = false;
innerDisposer?.();
outerDisposer?.();
};
});
},
config
);
super(subs, config);
}
override get $version(): ValVersion {
// resolve current value for the latest version
this.get();
return super.$version;
}
}
/**
* Creates a readonly val from a getter function and a listener function.
* If the value is a val, it will be auto-flattened.
*
* @param getValue A function that returns the current value.
* If the value is a val, it will be auto-flattened.
* @param listen A function that takes a notify function and returns a disposer.
* The notify function should be called when the value changes.
* @param config custom config for the val.
* @returns A readonly val with value of inner val.
*/
export const flattenFrom = <TValOrValue = any>(
getValue: () => TValOrValue,
listen: (notify: () => void) => ValDisposer | void | undefined,
config?: ValConfig<UnwrapVal<TValOrValue>>
): ReadonlyVal<UnwrapVal<TValOrValue>> =>
new FlattenFromImpl(getValue, listen, config);
return val$;
};

@@ -1,3 +0,3 @@

import type { ReadonlyValImpl } from "./readonly-val";
import type { ReadonlyVal, UnwrapVal, ValConfig } from "./typings";
import type { ValImpl } from "./val";

@@ -53,3 +53,3 @@ import { flattenFrom } from "./flatten-from";

TValOrValue = any,
TSrcVal extends ReadonlyValImpl = ReadonlyValImpl<TSrcValue>
TSrcVal extends ValImpl = ValImpl<TSrcValue>
>(

@@ -56,0 +56,0 @@ val: TSrcVal,

@@ -1,2 +0,1 @@

import { ReadonlyValImpl } from "./readonly-val";
import { Subscribers } from "./subscribers";

@@ -10,4 +9,5 @@ import type {

import { INIT_VALUE } from "./utils";
import { ValImpl } from "./val";
class FromImpl<TValue = any> extends ReadonlyValImpl<TValue> {
class FromImpl<TValue = any> extends ValImpl<TValue> {
public constructor(

@@ -14,0 +14,0 @@ getValue: () => TValue,

@@ -0,1 +1,7 @@

export { combine, type CombineValTransform } from "./combine";
export { derive, type DerivedValTransform } from "./derive";
export { flatten } from "./flatten";
export { flattenFrom } from "./flatten-from";
export { from } from "./from";
export { nextTick } from "./scheduler";
export type {

@@ -13,14 +19,13 @@ FlattenVal,

} from "./typings";
export { nextTick } from "./scheduler";
export { arrayShallowEqual, identity, isVal, strictEqual } from "./utils";
export { combine, type CombineValTransform } from "./combine";
export { derive, type DerivedValTransform } from "./derive";
export { flatten } from "./flatten";
export { flattenFrom } from "./flatten-from";
export { from } from "./from";
export { groupVals, readonlyVal } from "./readonly-val";
export { val } from "./val";
export { reaction, setValue, subscribe, unsubscribe } from "./value-enhancer";
export {
arrayShallowEqual,
attachSetter,
identity,
isVal,
reaction,
setValue,
strictEqual,
subscribe,
unsubscribe,
} from "./utils";
export { groupVals, readonlyVal, val } from "./val";

@@ -1,3 +0,58 @@

import type { ReadonlyVal, ValInputsValueTuple, ValVersion } from "./typings";
import type {
ReadonlyVal,
Val,
ValDisposer,
ValInputsValueTuple,
ValSubscriber,
ValVersion,
} from "./typings";
/**
* @deprecated
* Set the value of a val.
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`).
* Do nothing if the val is really `ReadonlyVal`.
*/
export const setValue = <TValue>(
val: ReadonlyVal<TValue>,
value: TValue
): void => (val as Val<TValue>).set?.(value);
/**
* Subscribe to value changes with immediate emission.
* @param val
* @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
*/
export const subscribe = <TValue>(
val: ReadonlyVal<TValue>,
subscriber: ValSubscriber<TValue>,
eager?: boolean
): ValDisposer => val.subscribe(subscriber, eager);
/**
* Subscribe to value changes without immediate emission.
* @param val
* @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
*/
export const reaction = <TValue>(
val: ReadonlyVal<TValue>,
subscriber: ValSubscriber<TValue>,
eager?: boolean
): ValDisposer => val.reaction(subscriber, eager);
/**
* Remove the given subscriber.
* Remove all if no subscriber provided.
* @param val
* @param subscriber
*/
export const unsubscribe = <TValue>(
val: ReadonlyVal<TValue>,
subscriber?: (...args: any[]) => any
): void => val.unsubscribe(subscriber);
/** Returns the value passed in. */

@@ -59,2 +114,13 @@ export const identity = <TValue>(value: TValue): TValue => value;

/**
* Attach a new setter to a val.
* @param val$ a readonly Val
* @param set a function that sets the value of val$
* @returns The same val$ with the new setter.
*/
export const attachSetter = <TValue>(
val$: ReadonlyVal<TValue>,
set: (this: void, value: TValue) => void
): Val<TValue> => (((val$ as Val<TValue>).set = set), val$ as Val<TValue>);
export const INIT_VALUE: any = {};

@@ -61,0 +127,0 @@

@@ -1,83 +0,221 @@

import type { NoInfer, ReadonlyVal, Val, ValConfig } from "./typings";
import type {
NoInfer,
ReadonlyVal,
Val,
ValConfig,
ValDisposer,
ValSetValue,
ValSubscriber,
ValVersion,
} from "./typings";
import { ReadonlyValImpl, ReadonlyValRefImpl } from "./readonly-val";
import { Subscribers } from "./subscribers";
import { SubscriberMode, Subscribers } from "./subscribers";
import { attachSetter, invoke, strictEqual } from "./utils";
export type { ValImpl };
/**
* Bare minimum implementation of a readonly val.
* Generally, you should use `readonlyVal` and `ReadonlyVal` instead of this class.
*/
export class ValImpl<TValue = any> implements ReadonlyVal<TValue> {
/**
* Manage subscribers for a val.
*/
readonly #subs: Subscribers<TValue>;
class ValImpl<TValue = any> extends ReadonlyValImpl<TValue> {
#config?: ValConfig<TValue>;
readonly #config?: ValConfig;
public constructor(currentValue: TValue, config?: ValConfig<TValue>) {
const get = () => currentValue;
readonly #eager?: boolean;
const subs = new Subscribers(get);
super(subs, config);
/**
* @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.set = (value: TValue) => {
if (!this.$equal?.(value, currentValue)) {
subs.dirty_ = true;
subs.newVersion_(config, value, currentValue);
currentValue = value;
subs.notify_();
}
};
this.$equal = (config?.equal ?? strictEqual) || void 0;
this.#eager = config?.eager;
}
public set: (this: void, value: TValue) => void;
public get $version(): ValVersion {
return this.#subs.version_;
}
public override get value() {
public get value(): TValue {
return this.get();
}
public override set value(value: TValue) {
public set value(value: TValue) {
this.set(value);
}
public override ref(): ReadonlyVal<TValue>;
public override ref(writable?: false): ReadonlyVal<TValue>;
public override ref(writable: true): Val<TValue>;
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue>;
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue> {
return writable
? new ValRefImpl(this, this.#config)
: new ReadonlyValRefImpl(this, this.#config);
public set(this: void, _value: TValue): void {
// do nothing
}
}
export class ValRefImpl<TValue = any> extends ReadonlyValRefImpl<TValue> {
readonly #source$: ValImpl<TValue>;
readonly #config?: ValConfig<TValue>;
public get: (this: void) => TValue;
public constructor(source$: ValImpl<TValue>, config?: ValConfig<TValue>) {
super(source$);
this.#source$ = source$;
this.#config = config;
this.set = source$.set;
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$;
}
public set: (this: void, value: TValue) => void;
public reaction(
subscriber: ValSubscriber<TValue>,
eager = this.#eager
): ValDisposer {
return this.#subs.add_(
subscriber,
eager ? SubscriberMode.Eager : SubscriberMode.Async
);
}
public override get value() {
return this.get();
public subscribe(
subscriber: ValSubscriber<TValue>,
eager = this.#eager
): ValDisposer {
const disposer = this.reaction(subscriber, eager);
invoke(subscriber, this.value);
this.#subs.dirty_ = false;
return disposer;
}
public override set value(value: TValue) {
this.set(value);
public $valCompute(subscriber: ValSubscriber<void>): ValDisposer {
return this.#subs.add_(subscriber, SubscriberMode.Computed);
}
public override ref(): ReadonlyVal<TValue>;
public override ref(writable?: false): ReadonlyVal<TValue>;
public override ref(writable: true): ReadonlyVal<TValue>;
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue>;
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue> {
return writable
? new ValRefImpl(this.#source$, this.#config)
: new ReadonlyValRefImpl(this.#source$, this.#config);
public unsubscribe(subscriber?: (...args: any[]) => any): void {
if (subscriber) {
this.#subs.remove_(subscriber);
} else {
this.#subs.clear_();
}
}
public dispose(): void {
this.#subs.clear_();
}
/**
* @returns the string representation of `this.value`.
*
* @example
* ```js
* const v$ = val(val(val(1)));
* console.log(`${v$}`); // "1"
* ```
*/
public toString(): string {
return String(this.value);
}
/**
* @returns the JSON representation of `this.value`.
*
* @example
* ```js
* const v$ = val(val(val({ a: 1 })));
* JSON.stringify(v$); // '{"a":1}'
* ```
*/
public toJSON(key: string): unknown {
const value = this.value as
| undefined
| null
| { toJSON?: (key: string) => unknown };
return value && value.toJSON ? value.toJSON(key) : value;
}
}
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);
}
}
/**
* Creates a readonly val with the given value.
*
* @returns A tuple with the readonly val and a function to set the value.
*/
export function readonlyVal<TValue = any>(): [
ReadonlyVal<NoInfer<TValue> | undefined>,
ValSetValue<NoInfer<TValue> | undefined>
];
/**
* Creates a readonly val with the given value.
*
* @param value Value for the val
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.
*/
export function readonlyVal(
value: [],
config?: ValConfig<any[]>
): [ReadonlyVal<any[]>, ValSetValue<any[]>];
/**
* Creates a readonly val with the given value.
*
* @param value Value for the val
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.
*/
export function readonlyVal<TValue = any>(
value: TValue,
config?: ValConfig<TValue>
): [ReadonlyVal<NoInfer<TValue>>, ValSetValue<NoInfer<TValue>>];
/**
* Creates a readonly val with the given value.
*
* @param value Optional value for the val
* @param config Optional custom config for the val.
* @returns A tuple with the readonly val and a function to set the value.
*/
export function readonlyVal<TValue = any>(
value?: TValue,
config?: ValConfig<TValue>
): [
ReadonlyVal<NoInfer<TValue | undefined>>,
ValSetValue<NoInfer<TValue | undefined>>
];
export function readonlyVal<TValue = any>(
value?: TValue,
config?: ValConfig<TValue | undefined>
): [
ReadonlyVal<NoInfer<TValue> | undefined>,
ValSetValue<NoInfer<TValue> | undefined>
] {
let currentValue = value;
const get = () => currentValue;
const subs = new Subscribers(get);
const set = (value: TValue | undefined): void => {
if (!val.$equal?.(value, currentValue)) {
subs.dirty_ = true;
subs.newVersion_(config, value, currentValue);
currentValue = value;
subs.notify_();
}
};
const val = new ValImpl(subs, config);
return [val, set];
}
/**
* Creates a writable val.

@@ -90,3 +228,3 @@ * @returns A val with undefined value.

* @param value Initial value.
* @param config Custom config.
* @param config Optional custom config.
*/

@@ -97,3 +235,3 @@ export function val(value: [], config?: ValConfig<any[]>): Val<any[]>;

* @param value Initial value.
* @param config Custom config.
* @param config Optional custom config.
*/

@@ -104,7 +242,75 @@ export function val<TValue = any>(

): Val<NoInfer<TValue>>;
/**
* Creates a writable val.
* @param value Initial value.
* @param config Optional custom config.
*/
export function val<TValue = any>(
value?: TValue,
config?: ValConfig<TValue | undefined>
): Val<NoInfer<TValue>>;
export function val<TValue = any>(
value?: TValue,
config?: ValConfig<TValue>
): Val<NoInfer<TValue>> {
return new ValImpl(value as TValue, config);
): Val<NoInfer<TValue | undefined>> {
const [val$, set] = readonlyVal(value, config);
return attachSetter(val$, set);
}
/**
* Takes an object of key-value pairs containing `ReadonlyVal` instances and their corresponding `ValSetValue` functions,
* and returns a tuple containing an array of the `ReadonlyVal` instances and a function to set their values.
*
* @example
* ```ts
* const [vals, setVals] = groupVals({
* a: readonlyVal(1),
* b: readonlyVal(2),
* c: readonlyVal(3),
* });
*
* vals.a.value; // 1
*
* setVals.a(2);
* ```
*
* This is useful for classes that have multiple `ReadonlyVal` instances as properties.
*
* ```ts
* export interface Foo$ {
* a: ReadonlyVal<number>;
* b: ReadonlyVal<number>;
* c: ReadonlyVal<number>;
* }
*
* export class Foo {
* public $: Foo$;
* private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> };
*
* public constructor() {
* const [vals, setVals] = groupVals({
* a: readonlyVal(1),
* b: readonlyVal(2),
* c: readonlyVal(3),
* });
*
* this.$ = vals;
* this.setVals = setVals;
* }
* ```
*/
export const groupVals = <TValues extends {}>(valPairs: {
[K in keyof TValues]: [ReadonlyVal<TValues[K]>, ValSetValue<TValues[K]>];
}): [
{ [K in keyof TValues]: ReadonlyVal<TValues[K]> },
{ [K in keyof TValues]: ValSetValue<TValues[K]> }
] => {
const vals = {} as { [K in keyof TValues]: ReadonlyVal<TValues[K]> };
const setters = {} as { [K in keyof TValues]: ValSetValue<TValues[K]> };
for (const key of Object.keys(valPairs) as (keyof TValues)[]) {
const [val, set] = valPairs[key];
vals[key] = val;
setters[key] = set;
}
return [vals, setters];
};

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