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

@amadeus-it-group/tansu

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@amadeus-it-group/tansu - npm Package Compare versions

Comparing version 0.0.17 to 0.0.18

361

index.cjs.js

@@ -13,2 +13,3 @@ 'use strict';

const symbolObservable = (typeof Symbol === 'function' && Symbol.observable) || '@@observable';
const oldSubscription = Symbol();
const noop = () => { };

@@ -21,19 +22,10 @@ const noopUnsubscribe = () => { };

};
const toSubscriberObject = (subscriber) => typeof subscriber === 'function'
? {
next: subscriber.bind(null),
pause: noop,
resume: noop,
_value: undefined,
_valueIndex: 0,
_paused: false,
}
: {
next: bind(subscriber, 'next'),
pause: bind(subscriber, 'pause'),
resume: bind(subscriber, 'resume'),
_value: undefined,
_valueIndex: 0,
_paused: false,
};
const toSubscriberObject = (subscriber) => ({
next: typeof subscriber === 'function' ? subscriber.bind(null) : bind(subscriber, 'next'),
pause: bind(subscriber, 'pause'),
resume: bind(subscriber, 'resume'),
_value: undefined,
_valueIndex: 0,
_paused: false,
});
const returnThis = function () {

@@ -62,15 +54,26 @@ return this;

};
const getNormalizedSubscribe = (input) => {
const store = 'subscribe' in input ? input : input[symbolObservable]();
return normalizeSubscribe(store);
};
const getValue = (subscribe) => {
let value;
subscribe((v) => (value = v))();
return value;
};
/**
* Returns a wrapper (for the given store) which only exposes the {@link Readable} interface.
* This converts any {@link StoreInput} to a {@link Readable} and exposes the store as read-only.
* Returns a wrapper (for the given store) which only exposes the {@link ReadableSignal} interface.
* This converts any {@link StoreInput} to a {@link ReadableSignal} and exposes the store as read-only.
*
* @param store - store to wrap
* @returns A wrapper which only exposes the {@link Readable} interface.
* @param extraProp - extra properties to add on the returned object
* @returns A wrapper which only exposes the {@link ReadableSignal} interface.
*/
function asReadable(input) {
const store = 'subscribe' in input ? input : input[symbolObservable]();
return {
subscribe: normalizeSubscribe(store),
function asReadable(store, extraProp) {
const subscribe = getNormalizedSubscribe(store);
const res = Object.assign(() => get(res), extraProp, {
subscribe,
[symbolObservable]: returnThis,
};
});
return res;
}

@@ -154,2 +157,4 @@ const triggerUpdate = Symbol();

};
const defaultReactiveContext = (store) => getValue(getNormalizedSubscribe(store));
let reactiveContext = defaultReactiveContext;
/**

@@ -167,7 +172,3 @@ * A utility function to get the current value from a given store.

*/
function get(store) {
let value;
asReadable(store).subscribe((v) => (value = v))();
return value;
}
const get = (store) => reactiveContext(store);
const createNotEqualCache = (valueIndex) => ({

@@ -211,8 +212,9 @@ [valueIndex]: false,

class Store {
#subscribers;
#cleanupFn;
#subscribersPaused;
#valueIndex;
#subscribers = new Set();
#cleanupFn = null;
#subscribersPaused = false;
#valueIndex = 1;
#value;
#notEqualCache;
#notEqualCache = createNotEqualCache(1);
#oldSubscriptions = new WeakMap();
/**

@@ -223,7 +225,2 @@ *

constructor(value) {
this.#subscribers = new Set();
this.#cleanupFn = null;
this.#subscribersPaused = false;
this.#valueIndex = 1;
this.#notEqualCache = createNotEqualCache(1);
this.#value = value;

@@ -258,5 +255,4 @@ }

}
#triggerUpdate() {
this.#cleanupFn?.[triggerUpdate]?.();
}
/** @internal */
[triggerUpdate]() { }
#notifySubscriber(subscriber) {

@@ -302,3 +298,3 @@ const notEqualCache = this.#notEqualCache;

*
* The paused state prevents derived stores (both direct and transitive) from recomputing their value
* The paused state prevents derived or computed stores (both direct and transitive) from recomputing their value
* using the current value of this store.

@@ -400,2 +396,10 @@ *

const subscriberObject = toSubscriberObject(subscriber);
const oldSubscriptionParam = subscriber?.[oldSubscription];
if (oldSubscriptionParam) {
const oldSubscriberObject = this.#oldSubscriptions.get(oldSubscriptionParam);
if (oldSubscriberObject) {
subscriberObject._value = oldSubscriberObject._value;
subscriberObject._valueIndex = oldSubscriberObject._valueIndex;
}
}
this.#subscribers.add(subscriberObject);

@@ -407,3 +411,3 @@ batch(() => {

else {
this.#triggerUpdate();
this[triggerUpdate]();
}

@@ -417,8 +421,11 @@ });

subscriberObject.resume = noop;
if (removed && this.#subscribers.size === 0) {
this.#stop();
if (removed) {
this.#oldSubscriptions.set(unsubscribe, subscriberObject);
if (this.#subscribers.size === 0) {
this.#stop();
}
}
};
unsubscribe[triggerUpdate] = () => {
this.#triggerUpdate();
this[triggerUpdate]();
this.#notifySubscriber(subscriberObject);

@@ -440,10 +447,9 @@ };

const subscribe = (subscriber) => {
toSubscriberObject(subscriber).next(value);
if (!subscriber?.[oldSubscription]) {
toSubscriberObject(subscriber).next(value);
}
return noopUnsubscribe;
};
normalizedSubscribe.add(subscribe);
return {
subscribe,
[symbolObservable]: returnThis,
};
return Object.assign(() => value, { subscribe, [symbolObservable]: returnThis });
}

@@ -529,7 +535,6 @@ class WritableStore extends Store {

const store = applyStoreOptions(new WritableStore(value), options);
return {
...asReadable(store),
return asReadable(store, {
set: store.set.bind(store),
update: store.update.bind(store),
};
});
}

@@ -542,10 +547,9 @@ function isSyncDeriveFn(fn) {

#isArray;
#stores;
#pending;
#storesSubscribeFn;
#pending = 0;
constructor(stores, initialValue) {
super(initialValue);
this.#pending = 0;
const isArray = Array.isArray(stores);
this.#isArray = isArray;
this.#stores = (isArray ? [...stores] : [stores]).map(asReadable);
this.#storesSubscribeFn = (isArray ? [...stores] : [stores]).map(getNormalizedSubscribe);
}

@@ -563,4 +567,4 @@ resumeSubscribers() {

const isArray = this.#isArray;
const storesArr = this.#stores;
const dependantValues = new Array(storesArr.length);
const storesSubscribeFn = this.#storesSubscribeFn;
const dependantValues = new Array(storesSubscribeFn.length);
let cleanupFn = null;

@@ -587,4 +591,4 @@ const callCleanup = () => {

};
const unsubscribers = storesArr.map((store, idx) => store.subscribe({
next: (v) => {
const unsubscribers = storesSubscribeFn.map((subscribe, idx) => {
const subscriber = (v) => {
dependantValues[idx] = v;

@@ -594,16 +598,14 @@ changed |= 1 << idx;

callDerive();
},
pause: () => {
};
subscriber.next = subscriber;
subscriber.pause = () => {
this.#pending |= 1 << idx;
this.pauseSubscribers();
},
resume: () => {
};
subscriber.resume = () => {
this.#pending &= ~(1 << idx);
callDerive();
},
}));
const clean = () => {
callCleanup();
unsubscribers.forEach(callFn);
};
};
return subscribe(subscriber);
});
const triggerSubscriberPendingUpdate = (unsubscriber, idx) => {

@@ -614,3 +616,3 @@ if (this.#pending & (1 << idx)) {

};
clean[triggerUpdate] = () => {
this[triggerUpdate] = () => {
let iterations = 0;

@@ -629,6 +631,9 @@ while (this.#pending) {

};
clean.unsubscribe = clean;
callDerive(true);
clean[triggerUpdate]();
return clean;
this[triggerUpdate]();
return () => {
this[triggerUpdate] = noop;
callCleanup();
unsubscribers.forEach(callFn);
};
}

@@ -660,2 +665,200 @@ }

}
/**
* Stops the tracking of dependencies made by {@link computed} and calls the provided function.
* After the function returns, the tracking of dependencies continues as before.
*
* @param fn - function to be called
* @returns the value returned by the given function
*/
const untrack = (fn) => {
const previousReactiveContext = reactiveContext;
try {
reactiveContext = defaultReactiveContext;
return fn();
}
finally {
reactiveContext = previousReactiveContext;
}
};
const callUnsubscribe = ({ unsubscribe }) => unsubscribe();
const callResubscribe = ({ resubscribe }) => resubscribe();
class ComputedStore extends Store {
#computing = false;
#skipCallCompute = false;
#versionIndex = 0;
#subscriptions = new Map();
#reactiveContext = (storeInput) => untrack(() => this.#getSubscriptionValue(storeInput));
constructor() {
super(undefined);
}
#createSubscription(subscribe) {
const res = {
versionIndex: this.#versionIndex,
unsubscribe: noop,
resubscribe: noop,
pending: false,
usedValueIndex: 0,
value: undefined,
valueIndex: 0,
};
const subscriber = (value) => {
res.value = value;
res.valueIndex++;
res.pending = false;
if (!this.#skipCallCompute && !this.#isPending()) {
this.#callCompute();
}
};
subscriber.next = subscriber;
subscriber.pause = () => {
res.pending = true;
this.pauseSubscribers();
};
subscriber.resume = () => {
res.pending = false;
if (!this.#skipCallCompute && !this.#isPending()) {
this.#callCompute();
}
};
res.resubscribe = () => {
res.unsubscribe = subscribe(subscriber);
subscriber[oldSubscription] = res.unsubscribe;
};
res.resubscribe();
return res;
}
#getSubscriptionValue(storeInput) {
let res = this.#subscriptions.get(storeInput);
if (res) {
res.versionIndex = this.#versionIndex;
res.unsubscribe[triggerUpdate]?.();
}
else {
res = this.#createSubscription(getNormalizedSubscribe(storeInput));
this.#subscriptions.set(storeInput, res);
}
res.usedValueIndex = res.valueIndex;
return res.value;
}
#callCompute(resubscribe = false) {
this.#computing = true;
this.#skipCallCompute = true;
try {
if (this.#versionIndex > 0) {
if (resubscribe) {
this.#subscriptions.forEach(callResubscribe);
}
if (!this.#hasChange()) {
this.resumeSubscribers();
return;
}
}
this.#versionIndex++;
const versionIndex = this.#versionIndex;
const previousReactiveContext = reactiveContext;
let value;
try {
reactiveContext = this.#reactiveContext;
value = this.compute();
}
finally {
reactiveContext = previousReactiveContext;
}
this.set(value);
for (const [store, info] of this.#subscriptions) {
if (info.versionIndex !== versionIndex) {
this.#subscriptions.delete(store);
info.unsubscribe();
}
}
}
finally {
this.#skipCallCompute = false;
this.#computing = false;
}
}
#isPending() {
for (const [, { pending }] of this.#subscriptions) {
if (pending) {
return true;
}
}
return false;
}
#hasChange() {
for (const [, { valueIndex, usedValueIndex }] of this.#subscriptions) {
if (valueIndex != usedValueIndex) {
return true;
}
}
return false;
}
resumeSubscribers() {
if (!this.#isPending()) {
super.resumeSubscribers();
}
}
/** @internal */
[triggerUpdate]() {
if (this.#computing) {
throw new Error('recursive computed');
}
let iterations = 0;
while (this.#isPending()) {
checkIterations(++iterations);
this.#skipCallCompute = true;
try {
for (const [, { pending, unsubscribe }] of this.#subscriptions) {
if (pending) {
unsubscribe[triggerUpdate]?.();
}
}
}
finally {
this.#skipCallCompute = false;
}
if (this.#isPending()) {
// safety check: if it is still pending after calling triggerUpdate,
// it will always be and this is an endless loop
break;
}
this.#callCompute();
}
}
onUse() {
this.#callCompute(true);
this[triggerUpdate]();
return () => this.#subscriptions.forEach(callUnsubscribe);
}
}
/**
* Creates a store whose value is computed by the provided function.
*
* @remarks
*
* The computation function is first called the first time the store is used.
* It can use the value of other stores or observables and the computation function is called again if the value of those dependencies
* changed, as long as the store is still used.
* Dependencies are detected automatically as the computation function gets their value either by calling the stores
* as a function (as it is possible with stores implementing {@link ReadableSignal}), or by calling the {@link get} function
* (with a store or any observable). If some calls made by the function should not be tracked as dependencies, it is possible
* to wrap them in a call to {@link untrack}.
* Note that dependencies can change between calls of the computation function. Internally, tansu will subscribe to new dependencies
* when they are used and unsubscribe from dependencies that are no longer used after the call of the computation function.
*
* @param fn - computation function that returns the value of the store
* @param options - store options
* @returns store containing the value returned by the computation function
*/
function computed(fn, options = {}) {
const Computed = class extends ComputedStore {
compute() {
return fn();
}
};
return asReadable(applyStoreOptions(new Computed(), {
...options,
onUse: undefined /* setting onUse is not allowed from computed */,
}));
}

@@ -666,2 +869,3 @@ exports.DerivedStore = DerivedStore;

exports.batch = batch;
exports.computed = computed;
exports.derived = derived;

@@ -671,2 +875,3 @@ exports.get = get;

exports.symbolObservable = symbolObservable;
exports.untrack = untrack;
exports.writable = writable;

@@ -16,6 +16,7 @@ /**

export declare const symbolObservable: typeof Symbol.observable;
declare const oldSubscription: unique symbol;
/**
* A callback invoked when a store value changes. It is called with the latest value of a given store.
*/
export type SubscriberFunction<T> = (value: T) => void;
export type SubscriberFunction<T> = ((value: T) => void) & Partial<Omit<SubscriberObject<T>, 'next'>>;
/**

@@ -47,2 +48,9 @@ * A partial {@link https://github.com/tc39/proposal-observable#api | observer} notified when a store value changes. A store will call the {@link SubscriberObject.next | next} method every time the store's state is changing.

resume: () => void;
/**
* @internal
* Value returned from a previous call to subscribe, and corresponding to a subscription to resume.
* This subscription must no longer be active. The new subscriber will not be called synchronously if
* the value did not change compared to the last value received in this old subscription.
*/
[oldSubscription]?: Unsubscriber;
}

@@ -103,2 +111,11 @@ /**

/**
* This interface augments the base {@link Readable} interface by adding the ability to call the store as a function to get its value.
*/
export interface ReadableSignal<T> extends Readable<T> {
/**
* Returns the value of the store. This is a shortcut for calling {@link get} with the store.
*/
(): T;
}
/**
* A function that can be used to update store's value. This function is called with the current value and should return new store value.

@@ -133,9 +150,17 @@ */

/**
* Returns a wrapper (for the given store) which only exposes the {@link Readable} interface.
* This converts any {@link StoreInput} to a {@link Readable} and exposes the store as read-only.
* Represents a store that implements both {@link ReadableSignal} and {@link Writable}.
* This is the type of objects returned by {@link writable}.
*/
export interface WritableSignal<T, U = T> extends ReadableSignal<T>, Writable<T, U> {
}
/**
* Returns a wrapper (for the given store) which only exposes the {@link ReadableSignal} interface.
* This converts any {@link StoreInput} to a {@link ReadableSignal} and exposes the store as read-only.
*
* @param store - store to wrap
* @returns A wrapper which only exposes the {@link Readable} interface.
* @param extraProp - extra properties to add on the returned object
* @returns A wrapper which only exposes the {@link ReadableSignal} interface.
*/
export declare function asReadable<T>(input: StoreInput<T>): Readable<T>;
export declare function asReadable<T, U = object>(store: StoreInput<T>, extraProp?: U): ReadableSignal<T> & U;
declare const triggerUpdate: unique symbol;
declare const queueProcess: unique symbol;

@@ -195,3 +220,3 @@ /**

*/
export declare function get<T>(store: StoreInput<T>): T;
export declare const get: <T>(store: StoreInput<T>) => T;
/**

@@ -236,2 +261,4 @@ * Base class that can be extended to easily create a custom {@link Readable} store.

private [queueProcess];
/** @internal */
protected [triggerUpdate](): void;
/**

@@ -255,3 +282,3 @@ * Compares two values and returns true if they are different.

*
* The paused state prevents derived stores (both direct and transitive) from recomputing their value
* The paused state prevents derived or computed stores (both direct and transitive) from recomputing their value
* using the current value of this store.

@@ -379,3 +406,3 @@ *

*/
export declare function readable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): Readable<T>;
export declare function readable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): ReadableSignal<T>;
/**

@@ -397,3 +424,3 @@ * A convenience function to create {@link Writable} store instances.

*/
export declare function writable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): Writable<T>;
export declare function writable<T>(value: T, options?: StoreOptions<T> | OnUseFn<T>): WritableSignal<T>;
/**

@@ -455,2 +482,30 @@ * Either a single {@link StoreInput} or a read-only array of at least one {@link StoreInput}.

export declare function derived<T, S extends StoresInput>(stores: S, options: SyncDeriveFn<T, S> | SyncDeriveOptions<T, S>, initialValue?: T): Readable<T>;
/**
* Stops the tracking of dependencies made by {@link computed} and calls the provided function.
* After the function returns, the tracking of dependencies continues as before.
*
* @param fn - function to be called
* @returns the value returned by the given function
*/
export declare const untrack: <T>(fn: () => T) => T;
/**
* Creates a store whose value is computed by the provided function.
*
* @remarks
*
* The computation function is first called the first time the store is used.
* It can use the value of other stores or observables and the computation function is called again if the value of those dependencies
* changed, as long as the store is still used.
* Dependencies are detected automatically as the computation function gets their value either by calling the stores
* as a function (as it is possible with stores implementing {@link ReadableSignal}), or by calling the {@link get} function
* (with a store or any observable). If some calls made by the function should not be tracked as dependencies, it is possible
* to wrap them in a call to {@link untrack}.
* Note that dependencies can change between calls of the computation function. Internally, tansu will subscribe to new dependencies
* when they are used and unsubscribe from dependencies that are no longer used after the call of the computation function.
*
* @param fn - computation function that returns the value of the store
* @param options - store options
* @returns store containing the value returned by the computation function
*/
export declare function computed<T>(fn: () => T, options?: Omit<StoreOptions<T>, 'onUse'>): ReadableSignal<T>;
export {};

@@ -11,2 +11,3 @@ /**

const symbolObservable = (typeof Symbol === 'function' && Symbol.observable) || '@@observable';
const oldSubscription = Symbol();
const noop = () => { };

@@ -19,19 +20,10 @@ const noopUnsubscribe = () => { };

};
const toSubscriberObject = (subscriber) => typeof subscriber === 'function'
? {
next: subscriber.bind(null),
pause: noop,
resume: noop,
_value: undefined,
_valueIndex: 0,
_paused: false,
}
: {
next: bind(subscriber, 'next'),
pause: bind(subscriber, 'pause'),
resume: bind(subscriber, 'resume'),
_value: undefined,
_valueIndex: 0,
_paused: false,
};
const toSubscriberObject = (subscriber) => ({
next: typeof subscriber === 'function' ? subscriber.bind(null) : bind(subscriber, 'next'),
pause: bind(subscriber, 'pause'),
resume: bind(subscriber, 'resume'),
_value: undefined,
_valueIndex: 0,
_paused: false,
});
const returnThis = function () {

@@ -60,15 +52,26 @@ return this;

};
const getNormalizedSubscribe = (input) => {
const store = 'subscribe' in input ? input : input[symbolObservable]();
return normalizeSubscribe(store);
};
const getValue = (subscribe) => {
let value;
subscribe((v) => (value = v))();
return value;
};
/**
* Returns a wrapper (for the given store) which only exposes the {@link Readable} interface.
* This converts any {@link StoreInput} to a {@link Readable} and exposes the store as read-only.
* Returns a wrapper (for the given store) which only exposes the {@link ReadableSignal} interface.
* This converts any {@link StoreInput} to a {@link ReadableSignal} and exposes the store as read-only.
*
* @param store - store to wrap
* @returns A wrapper which only exposes the {@link Readable} interface.
* @param extraProp - extra properties to add on the returned object
* @returns A wrapper which only exposes the {@link ReadableSignal} interface.
*/
function asReadable(input) {
const store = 'subscribe' in input ? input : input[symbolObservable]();
return {
subscribe: normalizeSubscribe(store),
function asReadable(store, extraProp) {
const subscribe = getNormalizedSubscribe(store);
const res = Object.assign(() => get(res), extraProp, {
subscribe,
[symbolObservable]: returnThis,
};
});
return res;
}

@@ -152,2 +155,4 @@ const triggerUpdate = Symbol();

};
const defaultReactiveContext = (store) => getValue(getNormalizedSubscribe(store));
let reactiveContext = defaultReactiveContext;
/**

@@ -165,7 +170,3 @@ * A utility function to get the current value from a given store.

*/
function get(store) {
let value;
asReadable(store).subscribe((v) => (value = v))();
return value;
}
const get = (store) => reactiveContext(store);
const createNotEqualCache = (valueIndex) => ({

@@ -209,8 +210,9 @@ [valueIndex]: false,

class Store {
#subscribers;
#cleanupFn;
#subscribersPaused;
#valueIndex;
#subscribers = new Set();
#cleanupFn = null;
#subscribersPaused = false;
#valueIndex = 1;
#value;
#notEqualCache;
#notEqualCache = createNotEqualCache(1);
#oldSubscriptions = new WeakMap();
/**

@@ -221,7 +223,2 @@ *

constructor(value) {
this.#subscribers = new Set();
this.#cleanupFn = null;
this.#subscribersPaused = false;
this.#valueIndex = 1;
this.#notEqualCache = createNotEqualCache(1);
this.#value = value;

@@ -256,5 +253,4 @@ }

}
#triggerUpdate() {
this.#cleanupFn?.[triggerUpdate]?.();
}
/** @internal */
[triggerUpdate]() { }
#notifySubscriber(subscriber) {

@@ -300,3 +296,3 @@ const notEqualCache = this.#notEqualCache;

*
* The paused state prevents derived stores (both direct and transitive) from recomputing their value
* The paused state prevents derived or computed stores (both direct and transitive) from recomputing their value
* using the current value of this store.

@@ -398,2 +394,10 @@ *

const subscriberObject = toSubscriberObject(subscriber);
const oldSubscriptionParam = subscriber?.[oldSubscription];
if (oldSubscriptionParam) {
const oldSubscriberObject = this.#oldSubscriptions.get(oldSubscriptionParam);
if (oldSubscriberObject) {
subscriberObject._value = oldSubscriberObject._value;
subscriberObject._valueIndex = oldSubscriberObject._valueIndex;
}
}
this.#subscribers.add(subscriberObject);

@@ -405,3 +409,3 @@ batch(() => {

else {
this.#triggerUpdate();
this[triggerUpdate]();
}

@@ -415,8 +419,11 @@ });

subscriberObject.resume = noop;
if (removed && this.#subscribers.size === 0) {
this.#stop();
if (removed) {
this.#oldSubscriptions.set(unsubscribe, subscriberObject);
if (this.#subscribers.size === 0) {
this.#stop();
}
}
};
unsubscribe[triggerUpdate] = () => {
this.#triggerUpdate();
this[triggerUpdate]();
this.#notifySubscriber(subscriberObject);

@@ -438,10 +445,9 @@ };

const subscribe = (subscriber) => {
toSubscriberObject(subscriber).next(value);
if (!subscriber?.[oldSubscription]) {
toSubscriberObject(subscriber).next(value);
}
return noopUnsubscribe;
};
normalizedSubscribe.add(subscribe);
return {
subscribe,
[symbolObservable]: returnThis,
};
return Object.assign(() => value, { subscribe, [symbolObservable]: returnThis });
}

@@ -527,7 +533,6 @@ class WritableStore extends Store {

const store = applyStoreOptions(new WritableStore(value), options);
return {
...asReadable(store),
return asReadable(store, {
set: store.set.bind(store),
update: store.update.bind(store),
};
});
}

@@ -540,10 +545,9 @@ function isSyncDeriveFn(fn) {

#isArray;
#stores;
#pending;
#storesSubscribeFn;
#pending = 0;
constructor(stores, initialValue) {
super(initialValue);
this.#pending = 0;
const isArray = Array.isArray(stores);
this.#isArray = isArray;
this.#stores = (isArray ? [...stores] : [stores]).map(asReadable);
this.#storesSubscribeFn = (isArray ? [...stores] : [stores]).map(getNormalizedSubscribe);
}

@@ -561,4 +565,4 @@ resumeSubscribers() {

const isArray = this.#isArray;
const storesArr = this.#stores;
const dependantValues = new Array(storesArr.length);
const storesSubscribeFn = this.#storesSubscribeFn;
const dependantValues = new Array(storesSubscribeFn.length);
let cleanupFn = null;

@@ -585,4 +589,4 @@ const callCleanup = () => {

};
const unsubscribers = storesArr.map((store, idx) => store.subscribe({
next: (v) => {
const unsubscribers = storesSubscribeFn.map((subscribe, idx) => {
const subscriber = (v) => {
dependantValues[idx] = v;

@@ -592,16 +596,14 @@ changed |= 1 << idx;

callDerive();
},
pause: () => {
};
subscriber.next = subscriber;
subscriber.pause = () => {
this.#pending |= 1 << idx;
this.pauseSubscribers();
},
resume: () => {
};
subscriber.resume = () => {
this.#pending &= ~(1 << idx);
callDerive();
},
}));
const clean = () => {
callCleanup();
unsubscribers.forEach(callFn);
};
};
return subscribe(subscriber);
});
const triggerSubscriberPendingUpdate = (unsubscriber, idx) => {

@@ -612,3 +614,3 @@ if (this.#pending & (1 << idx)) {

};
clean[triggerUpdate] = () => {
this[triggerUpdate] = () => {
let iterations = 0;

@@ -627,6 +629,9 @@ while (this.#pending) {

};
clean.unsubscribe = clean;
callDerive(true);
clean[triggerUpdate]();
return clean;
this[triggerUpdate]();
return () => {
this[triggerUpdate] = noop;
callCleanup();
unsubscribers.forEach(callFn);
};
}

@@ -658,3 +663,201 @@ }

}
/**
* Stops the tracking of dependencies made by {@link computed} and calls the provided function.
* After the function returns, the tracking of dependencies continues as before.
*
* @param fn - function to be called
* @returns the value returned by the given function
*/
const untrack = (fn) => {
const previousReactiveContext = reactiveContext;
try {
reactiveContext = defaultReactiveContext;
return fn();
}
finally {
reactiveContext = previousReactiveContext;
}
};
const callUnsubscribe = ({ unsubscribe }) => unsubscribe();
const callResubscribe = ({ resubscribe }) => resubscribe();
class ComputedStore extends Store {
#computing = false;
#skipCallCompute = false;
#versionIndex = 0;
#subscriptions = new Map();
#reactiveContext = (storeInput) => untrack(() => this.#getSubscriptionValue(storeInput));
constructor() {
super(undefined);
}
#createSubscription(subscribe) {
const res = {
versionIndex: this.#versionIndex,
unsubscribe: noop,
resubscribe: noop,
pending: false,
usedValueIndex: 0,
value: undefined,
valueIndex: 0,
};
const subscriber = (value) => {
res.value = value;
res.valueIndex++;
res.pending = false;
if (!this.#skipCallCompute && !this.#isPending()) {
this.#callCompute();
}
};
subscriber.next = subscriber;
subscriber.pause = () => {
res.pending = true;
this.pauseSubscribers();
};
subscriber.resume = () => {
res.pending = false;
if (!this.#skipCallCompute && !this.#isPending()) {
this.#callCompute();
}
};
res.resubscribe = () => {
res.unsubscribe = subscribe(subscriber);
subscriber[oldSubscription] = res.unsubscribe;
};
res.resubscribe();
return res;
}
#getSubscriptionValue(storeInput) {
let res = this.#subscriptions.get(storeInput);
if (res) {
res.versionIndex = this.#versionIndex;
res.unsubscribe[triggerUpdate]?.();
}
else {
res = this.#createSubscription(getNormalizedSubscribe(storeInput));
this.#subscriptions.set(storeInput, res);
}
res.usedValueIndex = res.valueIndex;
return res.value;
}
#callCompute(resubscribe = false) {
this.#computing = true;
this.#skipCallCompute = true;
try {
if (this.#versionIndex > 0) {
if (resubscribe) {
this.#subscriptions.forEach(callResubscribe);
}
if (!this.#hasChange()) {
this.resumeSubscribers();
return;
}
}
this.#versionIndex++;
const versionIndex = this.#versionIndex;
const previousReactiveContext = reactiveContext;
let value;
try {
reactiveContext = this.#reactiveContext;
value = this.compute();
}
finally {
reactiveContext = previousReactiveContext;
}
this.set(value);
for (const [store, info] of this.#subscriptions) {
if (info.versionIndex !== versionIndex) {
this.#subscriptions.delete(store);
info.unsubscribe();
}
}
}
finally {
this.#skipCallCompute = false;
this.#computing = false;
}
}
#isPending() {
for (const [, { pending }] of this.#subscriptions) {
if (pending) {
return true;
}
}
return false;
}
#hasChange() {
for (const [, { valueIndex, usedValueIndex }] of this.#subscriptions) {
if (valueIndex != usedValueIndex) {
return true;
}
}
return false;
}
resumeSubscribers() {
if (!this.#isPending()) {
super.resumeSubscribers();
}
}
/** @internal */
[triggerUpdate]() {
if (this.#computing) {
throw new Error('recursive computed');
}
let iterations = 0;
while (this.#isPending()) {
checkIterations(++iterations);
this.#skipCallCompute = true;
try {
for (const [, { pending, unsubscribe }] of this.#subscriptions) {
if (pending) {
unsubscribe[triggerUpdate]?.();
}
}
}
finally {
this.#skipCallCompute = false;
}
if (this.#isPending()) {
// safety check: if it is still pending after calling triggerUpdate,
// it will always be and this is an endless loop
break;
}
this.#callCompute();
}
}
onUse() {
this.#callCompute(true);
this[triggerUpdate]();
return () => this.#subscriptions.forEach(callUnsubscribe);
}
}
/**
* Creates a store whose value is computed by the provided function.
*
* @remarks
*
* The computation function is first called the first time the store is used.
* It can use the value of other stores or observables and the computation function is called again if the value of those dependencies
* changed, as long as the store is still used.
* Dependencies are detected automatically as the computation function gets their value either by calling the stores
* as a function (as it is possible with stores implementing {@link ReadableSignal}), or by calling the {@link get} function
* (with a store or any observable). If some calls made by the function should not be tracked as dependencies, it is possible
* to wrap them in a call to {@link untrack}.
* Note that dependencies can change between calls of the computation function. Internally, tansu will subscribe to new dependencies
* when they are used and unsubscribe from dependencies that are no longer used after the call of the computation function.
*
* @param fn - computation function that returns the value of the store
* @param options - store options
* @returns store containing the value returned by the computation function
*/
function computed(fn, options = {}) {
const Computed = class extends ComputedStore {
compute() {
return fn();
}
};
return asReadable(applyStoreOptions(new Computed(), {
...options,
onUse: undefined /* setting onUse is not allowed from computed */,
}));
}
export { DerivedStore, Store, asReadable, batch, derived, get, readable, symbolObservable, writable };
export { DerivedStore, Store, asReadable, batch, computed, derived, get, readable, symbolObservable, untrack, writable };
{
"name": "@amadeus-it-group/tansu",
"version": "0.0.17",
"version": "0.0.18",
"description": "tansu is a lightweight, push-based state management library. It borrows the ideas and APIs originally designed and implemented by Svelte stores",

@@ -5,0 +5,0 @@ "keywords": [

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