@typescript-package/data
Version:
A lightweight TypeScript library for basic data management.
451 lines (441 loc) • 12.7 kB
JavaScript
/**
* @description Manages the immutability states of `this` current instance.
* @export
* @abstract
* @class Immutability
*/
class Immutability {
/**
* @description
* @template Type
* @param {Type} object
* @returns {Readonly<Type>}
*/
static deepFreeze(object) {
if (object && typeof object === "object" && !Object.isFrozen(object)) {
Object.getOwnPropertyNames(object).forEach(prop => Immutability.deepFreeze(object[prop]));
Object.freeze(object);
}
return object;
}
/**
* @description Privately stored locked state as `true` if locked, otherwise `false`.
* @type {boolean}
*/
#locked = false;
/**
* @description Deeply freezes current `this` instance.
* @public
* @returns {this} Returns current instance.
*/
deepFreeze() {
if (this.isLocked()) {
throw new Error('Cannot freeze a locked object.');
}
Immutability.deepFreeze(this);
return this;
}
/**
* @description "Prevents the modification of existing property attributes and values, and prevents the addition of new properties."
* @public
* @returns {this} Returns current instance.
*/
freeze() {
if (this.isLocked()) {
throw new Error('Cannot freeze a locked object.');
}
Object.freeze(this);
return this;
}
/**
* @description Checks whether `this` current instance is frozen.
* @public
* @returns {boolean}
*/
isFrozen() {
return Object.isFrozen(this);
}
/**
* @description Checks whether the current instance is locked.
* @public
* @returns {boolean} Returns a `boolean` indicating whether current instance is locked.
*/
isLocked() {
return this.#locked === true;
}
/**
* @description Checks whether the object is mutable.
* @public
* @returns {boolean} True if the object is mutable, otherwise `false`.
*/
isMutable() {
return !this.isSealed() && !this.isFrozen() && !this.isLocked();
}
/**
* @description Checks whether `this` current instance is sealed.
* @public
* @returns {boolean} Returns a `boolean` indicating whether current instance is sealed.
*/
isSealed() {
return Object.isSealed(this);
}
/**
* @description Locks the object, means deeply freezes and blocks the `set()`, ensuring deep immutability.
* It combines the features of `Object.freeze`, but extends immutability to nested structures (deep immutability).
* @public
* @returns {this} Returns current instance.
*/
lock() {
Immutability.deepFreeze(this);
this.#locked = true;
return this;
}
/**
* @description "Prevents the modification of attributes of existing properties, and prevents the addition of new properties."
* @public
* @returns {this} Returns current instance.
*/
seal() {
if (this.isLocked()) {
throw new Error('Cannot seal a locked object.');
}
Object.seal(this);
return this;
}
/**
* @description Validates the ability to set the value.
* @protected
* @returns {this} Returns current instance.
*/
validate() {
if (this.isLocked()) {
throw new Error('Cannot set when data is locked.');
}
return this;
}
}
// Abstract.
/**
* @description The base abstraction with immutability for handling data-related classes.
* @export
* @abstract
* @class DataCore
* @template Type Represents the type of data value.
* @extends {Immutability}
*/
class DataCore extends Immutability {
/**
* @description Returns the `string` tag representation of the `DataCore` class when used in `Object.prototype.toString.call(instance)`.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return DataCore.name;
}
/**
* @inheritdoc
* @public
* @returns {this}
*/
lock() {
Immutability.deepFreeze(this.value);
super.lock();
return this;
}
}
/**
* @description The class to manage the value of generic type variable `Type`.
* @export
* @class Value
* @template Type The type of the privately stored `#value`.
*/
class Value {
/**
* @description Returns the `string` tag representation of the `Value` class when used in `Object.prototype.toString.call(instance)`.
* The `tag` getter returns the actual class name defined with the `Symbol.toStringTag()` of the child class.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return Value.name;
}
/**
* @description Returns the string tag of the current instance defined by the `Symbol.toStringTag`.
* @public
* @returns {string | undefined} The extracted class name, such as `'Value'`, or `undefined` if extraction fails.
*/
get tag() {
const tag = Object.prototype.toString.call(this).slice(8, -1);
return tag !== 'Object' ? tag : undefined;
}
/**
* @description Returns the privately stored value of generic type variable `Type`.
* @public
* @readonly
* @type {Type}
*/
get value() {
return this.#value;
}
/**
* @description Privately stored value of generic type variable `Type`.
* @type {Type}
*/
#value;
/**
* Creates an instance of child class.
* @constructor
* @param {Type} value The value of generic type variable `Type`.
*/
constructor(value) {
this.#value = value;
}
/**
* @description Sets the value of generic type variable `Type`.
* @protected
* @returns {this} Returns `this` current instance.
*/
set(value) {
this.#value = value;
return this;
}
}
// Abstract.
/**
* @description The `Data` class is a concrete class that wraps a value and provides methods for setting, retrieving, and destroying the value.
* @export
* @class Data
* @template Type
* @extends {DataCore<Type>}
*/
class Data extends DataCore {
/**
* @description Returns the `string` tag representation of the `Data` class when used in `Object.prototype.toString.call(instance)`.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return Data.name;
}
/**
* @description Returns the privately stored value of generic type variable `Type`.
* @public
* @readonly
* @type {Type}
*/
get value() {
return this.#value.value;
}
/**
* @description Privately stored value of class `Value`.
* @type {Value<Type>}
*/
#value;
/**
* Creates an instance of `Data` child class.
* @constructor
* @param {Type} value Initial data value of generic type variable `Type`.
*/
constructor(value) {
super();
this.#value = new Value(value);
}
/**
* @description Destroys the `Value` object by setting it to `null`.
* @public
* @returns {this} Returns the current instance.
*/
destroy() {
this.#value = null;
return this;
}
/**
* @description Sets the data value.
* @public
* @param {Type} value The data of `Type` to set.
* @returns {this} Returns the current instance.
*/
set(value) {
super.validate();
this.#value.set(value);
return this;
}
}
// Abstract.
/**
* @description The `NamedWeakData` class is a concrete class that manages data in a static `Map` where data is associated with a specified name.
* @export
* @class NamedWeakData
* @template [Type=any]
* @template {string} [Name='default']
* @extends {DataCore<Type>}
*/
class NamedWeakData extends DataCore {
name;
/**
* @description Gets the data from another instance.
* @public
* @static
* @template {string} Name
* @template Type
* @param {NamedWeakData<Name, Type>} instance Another instance from which to get the data.
* @param {Name} name The name from which get the data.
* @returns {Type} The value of the data stored in the given instance.
*/
static getFrom(instance, name) {
return NamedWeakData.#value.get(name)?.get(instance);
}
/**
* @description Returns the `string` tag representation of the `NamedWeakData` class when used in `Object.prototype.toString.call(instance)`.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return NamedWeakData.name;
}
/**
* @description A private static `Map` stores under specified `string` type name the data value instance in `WeakMap`.
* @static
* @readonly
* @type {Map<string, WeakMap<any, any>>}
*/
static #value = new Map();
/**
* @description Returns the privately stored data value from the specified name of static `Map`.
* @public
* @readonly
* @type {Type}
*/
get value() {
return NamedWeakData.#value.get(this.name)?.get(this);
}
/**
* Creates an instance of `NamedWeakData` child class.
* @constructor
* @param {Type} value Initial data value of `Type`.
* @param {string} [name='default'] The name under which the value is stored, defaults to `default`.
*/
constructor(value, name = 'default') {
super();
this.name = name;
NamedWeakData.#value.get(name) === undefined && NamedWeakData.#value.set(name, new WeakMap());
NamedWeakData.#value.get(name).set(this, value);
}
/**
* @description
* @public
* @returns {this} Returns `this` current instance.
*/
clear() {
NamedWeakData.#value.clear();
return this;
}
/**
* @description
* @public
* @returns {this} Returns `this` current instance.
*/
destroy() {
NamedWeakData.#value.get(this.name)?.delete(this);
this.clear();
return this;
}
/**
* @description Sets the data value.
* @public
* @param {Type} value The data of `Type` to set.
* @returns {this} Returns `this` current instance.
*/
set(value) {
super.validate();
NamedWeakData.#value.get(this.name)?.set(this, value);
return this;
}
}
// Abstract.
/**
* @description The `WeakData` class is a concrete class that stores data in a static `WeakMap`.
* @export
* @class WeakData
* @template Type
* @extends {DataCore<Type>}
*/
class WeakData extends DataCore {
/**
* @description Gets the data value from another instance.
* @public
* @static
* @template Type
* @param {DataStore<Type>} instance Another instance from which to get the data.
* @returns {Type} The value of the data stored in the given instance.
*/
static get(instance) {
return WeakData.#value.get(instance);
}
/**
* @description Returns the `string` tag representation of the `WeakData` class when used in `Object.prototype.toString.call(instance)`.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return WeakData.name;
}
/**
* @description A static, privately stored `WeakMap` used for associating each instance with its value.
* @readonly
* @type {WeakMap}
*/
static #value = new WeakMap();
/**
* @description
* @public
* @readonly
* @type {Type}
*/
get value() {
return WeakData.#value.get(this);
}
/**
* Creates an instance of `WeakData` child class.
* @constructor
* @param {Type} value Initial data value of `Type`.
*/
constructor(value) {
super();
WeakData.#value.set(this, value);
}
/**
* @description Destroys the value in a static `WeakMap`.
* @public
* @returns {this} Returns current instance.
*/
destroy() {
WeakData.#value.delete(this);
return this;
}
/**
* @description Sets the data value in a static `WeakMap`.
* @public
* @param {Type} value The data of `Type` to set.
* @returns {this} Returns current instance.
*/
set(value) {
super.validate();
WeakData.#value.set(this, value);
return this;
}
}
// Abstract class.
/*
* Public API Surface of data
*/
/**
* Generated bundle index. Do not edit.
*/
export { Data, DataCore, Immutability, NamedWeakData, Value, WeakData };
//# sourceMappingURL=typescript-package-data.mjs.map