A simple way to export and observe the state of module variable.
The inspiration comes from the case where I needed to "ask" the module about its internal state, but I didn't want to expose such information in the Application context (React Application). Not everything need to be widely available.
This library main purpose is to keep the code lean and in boundaries.
npm i module-state, bun add module-state
// someModuleWithExportedObservable.ts
import { Observable } from "module-state";
export const moduleVariable = new Observable("");
moduleVariable.set("Hello");// otherModule.ts
import { moduleVariable } from "./someModuleWithExportedObservable";
if (moduleVariable.hasBeenUpdated()) {
console.log("moduleVariable has been modified");
}
moduleVariable.set(`${moduleVariable.get()} world!`);
console.log(moduleVariable.get());
/** It will print:
*
* "moduleVariable has been modified"
* "Hello world!"
*/import { Observable } from "module-state";
export const moduleVariable = new Observable(true).readOnly();
try {
moduleVariable.set(false); // throws an error
} catch (e) {
console.log("read only Observable cannot be set");
}
/** It will print:
*
* "read only Observable cannot be set"
*/// someModuleWithExportedObservable.ts
import { Observable } from "module-state";
export const moduleVariable = new Observable(3);
const editableModuleVariable = moduleVariable.seal();
export function doSomething(value: number) {
editableModuleVariable.set(2 * value);
}// otherModule.ts
import { moduleVariable, doSomething } from "./someModuleWithExportedObservable";
if (moduleVariable.isReadOnly()) {
console.log("moduleVariable is read only variable");
}
console.log(`moduleVariable is set to ${moduleVariable.get()}`);
try {
moduleVariable.set(4);
} catch (e) {
console.log("moduleVariable cannot be externally set");
}
doSomething(5);
console.log(`moduleVariable is set to ${moduleVariable.get()}`);
/** It will print:
*
* "moduleVariable is read only variable"
* "moduleVariable is set to 3"
* "moduleVariable cannot be externally set"
* "moduleVariable is set to 10"
*/import { Observable } from "module-state";
export const moduleVariable = new Observable({ a: 1, b: [1, 2] });
moduleVariable.next().then(v => console.log(`moduleVariable b[2] contains ${v.b[v.b.length - 1]}`));
moduleVariable.get().b.push() = 123;
moduleVariable.get().b.push() = 124;
/** It will print:
*
* "moduleVariable b[2] contains 123"
*/import { Observable } from "module-state";
export const moduleVariable = new Observable({ a: 1, b: [1, 2] });
moduleVariable.onChange(v => console.log(`moduleVariable b[2] contains ${v.b[2]}`));
moduleVariable.get().b.push() = 123;
moduleVariable.get().b[2] = 124;
moduleVariable.set({ a: 1, b: [1, 2, 125] });
/** It will print:
*
* "moduleVariable b[2] contains 123"
* "moduleVariable b[2] contains 124"
* "moduleVariable b[2] contains 125"
*/import { Observable } from "module-state";
const moduleVariableNumber = new Observable(Number(3));
const moduleVariableString = new Observable("Hello");
console.log(moduleVariableNumber.typeOf());
console.log(moduleVariableNumber.instanceOf(Number));
console.log(moduleVariableString.typeOf());
console.log(moduleVariableString.instanceOf(String));
/**
* It will print:
*
* "number"
* true
* "string"
* true
*/typeof newnew Observable("").get() === "string";
typeof new Observable(-1).get() === "number";
Observable(new Number(2)).get() instanceof Number;
typeof new Observable(true).get() === "boolean";
// returns a reference to the object
typeof new Observable({ a: "", b: [1, 2] }).get() === "object";try {
new Observable(-1).set("");
} catch (e) {
console.log("Numeric Observable cannot be set as a string");
}
try {
new Observable(true).set({});
} catch (e) {
console.log("Boolean Observable cannot be set an object");
}
// not any type checking is performed for objects at runtime
new Observable({}).set({ a: 4, b: ["1a", "2a", "3a"] });
/** It will print:
*
* "Numeric Observable cannot be set as a string"
* "Boolean Observable cannot be set an object"
*/strictlyEditable method will not allow to mark the Observable as read only. So nobody will change the Observable's behaviour
export const moduleVariable = new Observable("").strictlyEditable();
try {
moduleVariable.readOnly();
} catch (e) {
console.log("strictlyEditable Observable cannot be set as read only");
}
try {
moduleVariable.seal();
} catch (e) {
console.log("strictlyEditable Observable cannot be sealed");
}
moduleVariable.isReadOnly() === false;const moduleVariable = new Observable("");
const editableModuleVariable = moduleVariable.seal();
try {
editableModuleVariable.readOnly();
} catch (e) {
console.log("strictlyEditable Observable cannot be set as read only");
}const moduleVariable = new Observable("").readOnly();
try {
moduleVariable.seal();
} catch (e) {
console.log("read only Observable cannot be sealed");
}const moduleVariable = new Observable("").readOnly();
moduleVariable.readOnly();
moduleVariable.readOnly();
moduleVariable.readOnly();import { Observable } from "module-state";
try {
new Observable(undefined); // throws an error
} catch (e) {
console.log("undefined is not accepted as an initial value");
}
try {
new Observable(null); // throws an error
} catch (e) {
console.log("null is not accepted as an initial value");
}
/** It will print:
*
* "undefined is not accepted as an initial value"
* "null is not accepted as an initial value"
*/π² Add bundling to import from CDN (vanilla js) -> umd, esm
π² Improve Continuous Integration
π² Add Changelog, Code of Conduct
π² Automatic testing and linting
π² Allow to pass class/function object with it's own onChange method. This become read only (sealed)
automatically (Object changes will be done through object's properties). In this case, when onChange is triggered, the
Observable will return true as a result of hasBeenUpdated method call. get returns reference to the object itself in
this case.
π² Allow to pass a replacer function as the second argument. The replacer function allows you to transform or filter the values which are not accepted now because they cannot be stringified by JSON.stringify function.