
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
rxjs-idsets
Advanced tools
A number of Typescript Sets that publish changes in their state (entries added, created, updated or deleted) using RxJS Observables.
If you like to work with Typescript, RxJS and whished you could observe changes to Set or Map like objects or classes, this library might be for you because that is exactly what it does.
It also provides 'live' union, intersection and difference set operations. I.e. if a source set of a set operation changes, the result of that set operation is updated automatically.
It requires values that implement an IdObject interface (which is simply an object with an id property, see the introduction for more details).
It encourages the use of immutable objects as it only detects updates of existing values if the new value is a different object (the id is the same, but for the IdObject newIdValue !== existingIdValue is true).
Otherwise it provides all the functionality (and more) from Typescript Sets and adds several RxJS Observables to keep you informed of changes.
Apart from Typescripts tslib there are no external dependencies.
This library provides a number of IdSet classes, which are observable extensions of the javascript Set object.
The values in an IdSet are objects that implement the IdObject interface:
interface IdObject<Id = string> {
id: Id
}
The uniqueness of a value in an IdSet is defined by the value of its id. It functions as a javascript Map where the key is the id property of the value (under water it actually is).
If you add an element to an IdSet with an id that already exists in the set, the existing value will be updated to the new value and the change to the IdSet will be published through the appropriate Observables.
This library provides the following IdSet classes:
IdSet is the basic observable SetUnionIdSet is a computed IdSet that perfoms the union set operation.
It computes and automatically updates the union of multiple source IdSet's.IntersectionIdSet is a computed IdSet that performs the intersection set operation.
It computes and automatically updates the intersection of multiple source IdSet's.SubtractionIdSet is a computed IdSet that performs the subtract set operation.
It computes and automatically updates the subtraction of multiple subtract IdSet's from the source IdSet.ContainerIdSet is an IdSet where you add values to one or more containers.
You can add, remove and update containers and values independant of one another and be notified of changes.
It is a powerful concept, see the ContainerIdSet section for more details.ReadOnlyIdSet is a readonly version of the IdSet. It can be used as base class to create your own computed IdSet's.Changes to the above classes can be observed with the following RxJS Observable properties:
all$ returns all values that are currently in the set.create$ returns only the new values, values with an id that does not already exist in the set.update$ returns only the changed values, values with an id that does exist in the set and is not a reference to to the existing value (needs to be newvalue !== existingvalue).delete$ returns the values that will be deleted from the set.For convenience the following Observable properties are also provided:
add$ returns all values that will be added (both created and updated) to the set.allAdd$ returns all values currently in the set and then switches to add$.delta$ returns a structure that can contain one or more create'd, update'd and/or delete'd value's.This library was created as a more 'pure' Observable version of my rxjs-supersets library.
For the latest changes and the current version see the Change log.
Nothing explains a library better than a few well documented examples, so here they come (I hope the examples are indeed documented enough).
The included examples are basic examples on how to use the IdSet classes, a more elaborate 'real life' example is planned for the future.
The IdSet is the basic class this library is built upon, it provides all normal Typescript Set operations
// create a new set containing 3 values, values implement IdObject interface
const exampleSet = new IdSet([value1, value2, value3]);
// subscribe to an observable that emits all values added to the set
exampleSet.add$.subscribe(value => console.log('created or updated', value));
// add a new value to the set
// where exampleSet already contains [value1, value2, value3]
exampleSet.add(value4);
// exampleset now contains [value1, value2, value3, value4]
// the exampleset.create$, .add$ and .allAdd$ observables publish value4
// the exampleset.delta$ and allDelta$ publish { create: value4 }
// update an existing value
exampleSet.add(value1update);
// exampleset now contains [value1update, value2, value3, value4]
// the exampleset.update$, .add$ and .allAdd$ observables publish value1update
// the exampleset.delta$ and allDelta$ publish { update: value1update }
// delete a value from the set
exampleSet.delete(value1.id);
// exampleset now contains [value2, value3, value4]
// the exampleset.delete$ observable publishes value1
// the exampleset.delta$ and .allDelta$ publish { delete: value1 }
// replace the contents of the set
exampleSet.replace([value1, value2update, value3]);
// exampleset now contains [value1, value2update, value3]
// the exampleset.create$ observable publishes value1
// the exampleset.update$ observable publishes value2update
// the exampleset.add$ and .allAdd$ observables publish value1 and value2update
// the exampleset.delete$ observable publishes value4
// delete all content from the set
exampleSet.clear();
// exampleset now contains []
// the exampleset.delete$ observable publishes value1, value2update and value3
// close all subscritions to the set
exampleSet.complete();
// completes all existing and new subscriptions ('unsubscribes' them)
// all existing and new subscriptions will no longer receive added, updated or deleted values
exampleSet.pause();
// pauses all observable updates,
// useful when batch processing multiple add, update and/ore delete actions
// automatically removes overlapping/multiple add remove and update actions on the same value
exampleset.resume();
// resumes publishing of observables, immediately publishes all paused updates
A more complete example for the IdSet can be found in example1.ts
The UnionIdSet is a computed IdSet that is the live representation of the mathematical union of two
or more IdSets.
Updates in one of the source sets are immediately processed in the union set. The UnionIdSet observables publish the changes.
const set1 = new IdSet([value1, value2]);
const set2 = new IdSet([value2, value3]);
const unionSet = new UnionIdSet([set1, set2]);
// unionSet: [value1, value2, value3]
// subscribe to allAdd$
unionSet.allAdd$.subscribe(value => console.log('already present, created or updated', value));
set1.add(value4);
// unionSet: [value1, value2, value3, value4] value4 added to the union
set1.delete(value2);
// unionSet: [value1, value2, value3, value4] because value2 is still in set2
set1.delete(value1);
// unionSet: [value2, value3, value4] because value1 is not in another union source it is deleted
unionSet.add(set3);
unionSet.add([set3, set4]);
// adds extra sets to the unionSet
unionSet.delete(set1);
// removes sets from the unionSet
The IntersectionIdSet is a computed IdSet that is the live representation of the mathematical intersection of two or more IdSets.
Updates in one of the source sets are immediately processed in the intersection set. The IntersectionIdSet observables publish the changes.
const set1 = new IdSet([value1, value2]);
const set2 = new IdSet([value2, value3]);
const intersectionSet = new IntersectionIdSet([set1, set2]);
// unionSet: [value2]
// subscribe to delete$
intersectionSet.delete$.subscribe(value => console.log('deleted', value));
set1.add(value4);
// intersectionSet: [value2] value4 is not in all sources, so not added to the intersection
set1.delete(value1);
// intersectionSet: [value2] because value3 was not in the intersection
set1.add(value3);
// intersectionSet: [value2, value3] because value3 is now in all intersection sources
intersectionSet.add(set3);
intersectionSet.add([set3, set4]);
// adds extra sets to the intersectionSet
intersectionSet.delete(set1);
// removes sets from the intersectionSet
The DifferenceIdSet is a computed IdSet that is the live representation of the a source IdSet from which the elements contained in one or more other IdSets are subtracted.
Updates in one of the sets are immediately processed in the difference set. The DifferenceIdSet observables publish the changes.
const sourceSet = new IdSet([value1, value2, value3])
const subtractSet1 = new IdSet([value1]);
const subtractSet2 = new IdSet([value2]);
const differenceSet = new DifferenceIdSet(sourceSet, [set1, set2]);
// differenceResultSet: [value3]
differenceSet.add(set3);
differenceSet.add([set3, set4]);
// adds extra subtract sets to the differenceSet
differenceSet.delete(set1);
// removes subtract sets from the differenceSet
differenceSet.replace(newSource);
// replaces the source set of the differenceSet with the newSource set
// subscribe to create$
differenceResultSet.create$.subscribe(value => console.log('created new', value));
differenceResultSet.add(value4);
// differenceResultSet: [value3, value4] because value4 is not present in one of the subtractSets
subtractSet1.add(value4);
// differenceResultSet: [value3] because value4 is now present in one of the subtractSets
subtractSet1.delete(value1);
// differenceResultSet: [value1, value3] because value1 is no longer present in one of the subtractSets
The ContainerIdSet is an IdSet that consists of named subsets
const container = new ContainerIdSet();
// add value1 to the IdSet 'set1', create the IdSet if it does not altready exist
container.add(value1, 'set1');
// create a new empty IdSet inside the container if a set with that name does not exist
const set2 = container.getSet('set2');
// if the set already contains values get the existing IdSet
const set1 = container.getSet('set1');
// you can add multiple values to multiple IdSets at once if you want to
container.add([value2, value3], ['set1', 'set2', 'set3']);
// container now contains [value1, value2 value3]
// set1 now contains [value1, value2, value3]
// set2 now contains [value2, value3]
// there is also a 'set3' in the container containing [value2, value3]
// add or replace value3 only in the specified IdSets
// remove from all other sets if it already exists there
container.addExclusive(value3, ['set2', 'set3']);
// container still contains [value1, value2 value3]
// set1 now contains [value1, value2]
// set2 now contains [value2, value3]
// there is now also a 'set3' in the container with [value3]
container.delete(value2.id, 'set2');
// set2 now contains [value3]
container.delete(value1.id);
// container now contains [value2 value3]
// set2 now contains []
container.setsBelongedTo(value3.id);
// should return a Set containing ['set1', 'set3']
// there are methods to create union, intersection and subtraction sets from sets
const unionIdSet = container.union(['set1', 'set2', 'set3']);
const intersectionIdSet = container.intersection(['set1', 'set2', 'set3']);
const differenceIdSet = container.difference('set1', ['set2', 'set3']);
const complementIdSet = container.complement(['set2', 'set3']);
// complement returns a differenceIdSet of the specified sets with the container
The all important 'if all else fails, read the manual' command reference.
I have tried to make the IdSets as self explaining as possible from within an IDE
(VS Code in my case), but this reference might help.
This reference is a 'minimal' reference as in only Class specific properties and methods and overridden methods with changed or extended functionality will be described here, unchanged parent methods and properties will be described in the parent class only.
The BaseIdSet is not very useful in itself, but it contains all the basic functionality needed for the IdSet and other subclasses to function, it can be used as a base for your own custom IdSets.
It is an IdSet that is readonly. This means that it provides no way to change its contents by itself. when you create a subclass, you can use the addValue and deleteId protected methods to add and delete values and automatically publish relevant updates through the obserbvables.
The methods and properties that define the basic functionality of the IdSet classes are described below.
constructor(values?: Iterable<IdValue>, config?: IdSetConfig)type IdSetConfig<SourceIdValue, ResultIdValue=SourceIdValue> = {
cloneValues?: boolean;
filter?: (value: SourceIdValue, idSet: BaseIdSet) => boolean;
transform?: (value: SourceIdValue, idSet: BaseIdSet) => ResultIdValue;
}
cloneValues when defined clones the values passed in values with structuredClone()filter when defined filters out all IdValues that the filter function returns false to,
the idSet parameter contains a reference to this idSettransform when defined transforms the source IdValues and adds the transformed result to
the IdSet, it maintains the original id as identifier (even though it can be changed in the transformation), the idSet parameter contains a reference to this idSetstructuredClone() if cloneValues is trueall$: Observable<IdValue>create$: Observable<IdValue>update$: Observable<IdValue>delete$: Observable<IdValue>add$: Observable<IdValue>allAdd$: Observable<IdValue>add$.delta$(): Observable<Readonly<DeltaValue<IdValue>>>DeltaValue structure. A DeltaValue structure combines one or more create, update and delete values.allDelta$(): Observable<Readonly<DeltaValue<IdValue>>>DeltaValue structure. A DeltaValue structure combines one or more create, update and delete values. The first result contains all current values of the set in the create property of the DeltaValue.observedtrue when the BaseIdSet is observed (subscribed to), false otherwise.pause()resume() method.resume()pause()
will be published after calling the resume() method. Later updates will be published immediately.complete()all$ Observable will still function.Set properties and methodsThe methods and properties that are more or less identical to the default javascript Set class.
See below, no description apart from the type annotation is given.
size: numbervalues(): IterableIterator<IdValue>forEach(fn: (...) => void)get(key: Id): IdValuehas(key: Id): booleankeys(): IterableIterator<Id>entries(): IterableIterator<[Id, IdValue]>[Symbol.iterator](): IterableIterator<IdValue>There are a few protected methods that can be used when creating your own IdSet subclass.
protected addValue(value: IdValue)protected deleteId(id: Id)protected clear()This is the basic 'bread and butter' class of the IdSet classes (that is why it is called IdSet).
It extends the BaseIdSet.
See the example1.ts file for a complete example of the IdSet.
The IdSet class extends the BaseIdClass with the methods described below.
add(values: OneOrMore<IdValue>)delete(ids: OneOrMore<Id>): booleanreplace(values: OneOrMore<IdValue>, cloneValues = false)structuredClone() if cloneValues is true.clear()pause()resume()The UnionIdSet is a live union of the source IdSets defined in the constructor.
It extends the BaseIdSet.
The UnionIdSet is a 'live' representation of that union. I.e. if the content of a source IdSet changes it automatically updates the content of the UnionIdSet, see the example below:
source1 = new IdSet([value1, value2]);
source2 = new IdSet([value2, value3]);
source3 = new IdSet([value3, value5]);
unionIdSet = new UnionIdSet([source1, source2]); //contains [value1, value2, value3]
source2.add(value4);
// unionIdSet now contains [value1, value2, value3, value4]
unionIdSet.add(source3);
// unionIdSet now contains [value1, value2, value3, value4, value5]
unionIdSet.delete(source1);
// unionIdSet now contains [value2, value3, value4, value5]
constructor(sourceSets: Iterable<BaseIdSet>, config?: IdSetConfig)IdSets the UnionIdSet operates upon at construction.add(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)delete(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)readonly sourceSets: Iterable<BaseIdSet>UnionIdSet operates uponadd(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)UnionIdSetdelete(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)UnionIdSetThe IntersectionIdSet is a live intersection of the source IdSets defined in the constructor.
It extends the BaseIdSet.
The IntersectionIdSet is a 'live' representation of that intersection. I.e. if the content of a source IdSet changes it automatically updates the content of the IntersectionIdSet, see the example below:
source1 = new IdSet([value1, value2]);
source2 = new IdSet([value2, value3]);
intersectionIdSet = new IntersectionIdSet([source1, source2]); //contains [value2]
source2.add(value1);
// intersectionIdSet now contains [value1, value2]
constructor(sourceSets: Iterable<BaseIdSet>, config?: IdSetConfig)IdSets the IntersectionIdSet operates upon at construction.add(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)delete(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)readonly sourceSets: Iterable<BaseIdSet>IntersectionIdSet operates uponadd(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)IntersectionIdSetdelete(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)IntersectionIdSetThe DifferenceIdSet is the live difference between the source IdSet and other sets defined in the constructor.
It extends the BaseIdSet.
The DifferenceIdSet is a 'live' representation of that difference. I.e. if the content of a source or other IdSet changes it automatically updates the content of the DifferenceIdSet, see the example below:
source = new IdSet([value1, value2, value3, value4]);
other1 = new IdSet([value3]);
other2 = new IdSet([value3, value4]);
intersectionIdSet = new DifferenceIdSet(source, [other1, other2]); //contains [value1, value2]
other1.add(value1);
// DifferenceIdSet now contains [value2]
constructor(sourceSet: IdSet, otherSets: Iterable<BaseIdSet>, config?: IdSetConfig)IdSets the DifferenceIdSet operates upon at construction.add(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)delete(idSets: OneOrMore<BaseIdSet<IdValue, Id>>)replace(sourceSet: IdSet)DifferenceIdSet operates upon.readonly sourceSet: BaseIdSetDifferenceIdSet operates uponreadonly othersets: Iterable<BaseIdSet>DifferenceIdSet operates uponA ContainerIdSet consists of one or more IdSets identified by a SetId.
The ContainerIdSet itself is also an IdSet where the values are the union of all the sets it contains.
It extends the IdSet.
It can add, delete, replace values etc. and subscribe to changes with the create$, delete$ etc.
Observables.
A value in a ContainerIdSet only exists if the value is alse present in one or more of its contained
sets.
A few lines of example code:
const container = new ContainerIdSet();
container.add([value1, value2], 'set1');
container.add(value3, ['set2', 'set3']);
const set1 = container.getSet('set1');
set1.delete(value2.id);
set1.allAdd$.subscribe(value => console.log(`Added to set2: ${value}`));
constructor(values?: OneOrMore<[IdValue, Iterable<SetId>]>, cloneValues = false)export() method to create values for the constructor to duplicate an existing
ContainerIdSet.structuredClone() if cloneValues is true.sets: ReadonlyMap<SetId, IdSet>add(values: OneOrMore<IdValue>, setIds?: OneOrMore<SetId>)delete(ids: OneOrMore<Id>, setIds?: OneOrMore<SetId>)ContainerIdSet.ContainerIdSet
it will also be removed from the ContainerIdSet.replace(values: OneOrMore<[IdValue, Iterable<SetId>]>, cloneValues = false)ContainerIdSet with the specified values.ContainerIdSet and all its existing sets will be cleared.export(): IterableIterator<[IdValue, Iterable<SetId>]>ContainerIdSet in a format that the constructor and replace
method understand.addExclusive(values: OneOrMore<IdValue>, sets?: OneOrMore<SetId>)complete()ContainerIdSet and all category IdSetssetsBelongedTo(id: Id): ReadonlySet<SetId> | undefinedSet containing the SetIds for the IdSets the value with this id is member of
or undefined if it is not member of any contained set.clear(sets?: OneOrMore<SetId>)ContainerIdSet.detachSet(setIds: OneOrMore<SetId>)ContainerIdSetgetSet(setId: SetId): IdSetcreateUnion(sets: Iterable<SetId>)createIntersection(sets: Iterable<SetId>)createDifference(category: SetId, subtractedCategories: OneOrMore<SetId>)createComplement(subtractedCategories: OneOrMore<SetId>)FAQs
A number of Typescript Sets that publish changes in their state (entries added, created, updated or deleted) using RxJS Observables.
We found that rxjs-idsets demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.