value-object-cache
Version:
A value object cache that can be used to make value objects behave like primitive types, i.e. if two variables `a` and `b` point to instances of the same class and have the same value, then `a === b`, otherwise `a !== b`.
144 lines (143 loc) • 6.29 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ValueObject: () => ValueObject,
isPrimitive: () => isPrimitive,
isValue: () => isValue,
isValueArray: () => isValueArray,
isValueObject: () => isValueObject,
valueObjectCache: () => valueObjectCache
});
module.exports = __toCommonJS(src_exports);
var VALUE_OBJECT_BRAND = Symbol();
var _a;
_a = VALUE_OBJECT_BRAND;
var ValueObject = class {
constructor(props, values) {
this.props = props;
this[_a] = true;
Object.freeze(this.props);
return valueObjectCache.getObject(this.constructor, values, () => this);
}
};
function isPrimitive(x) {
return typeof x !== "function" && (typeof x !== "object" || x === null);
}
function isValueArray(x) {
return Array.isArray(x) && x.every(isValue);
}
function isValueObject(x) {
return x instanceof ValueObject;
}
function isValue(x) {
return isPrimitive(x) || isValueArray(x) || isValueObject(x);
}
var _rootNode, _finalizationRegistry, _ValueObjectCache_instances, get_fn, _a2;
var valueObjectCache = new (_a2 = class {
constructor() {
__privateAdd(this, _ValueObjectCache_instances);
__privateAdd(this, _rootNode, { children: null, instanceRef: null });
__privateAdd(this, _finalizationRegistry, new FinalizationRegistry((path) => {
var _a3, _b;
const walkedNodes = [
{ currentNode: __privateGet(this, _rootNode), parentNode: null, parentKey: null }
];
let node = __privateGet(this, _rootNode);
for (const key of path) {
const childNode = (_a3 = node.children) == null ? void 0 : _a3.get(key);
if (!childNode) break;
walkedNodes.push({ currentNode: childNode, parentNode: node, parentKey: key });
node = childNode;
}
walkedNodes.reverse();
for (const { currentNode, parentNode, parentKey } of walkedNodes) {
if (currentNode.children && !currentNode.children.size) currentNode.children = null;
if (currentNode.instanceRef && !currentNode.instanceRef.deref()) currentNode.instanceRef = null;
if (currentNode.children || currentNode.instanceRef) break;
(_b = parentNode == null ? void 0 : parentNode.children) == null ? void 0 : _b.delete(parentKey);
}
}));
}
/** Look for an instance of the provided class constructor matching the provided values. If a matching instance is
* found then it is returned, otherwise the factory function is called to create a new instance, which is then stored
* in the cache and returned - all future calls to this method made with the same constructor and values will return
* this instance until it is garbage-collected. */
getObject(constructor, values, factory) {
return __privateMethod(this, _ValueObjectCache_instances, get_fn).call(this, constructor, values.map((v) => this.getValue(v)), () => Object.freeze(factory()));
}
/** Look for an {@link Array} containing a specific list of values in the cache. If a matching {@link Array} is found
* then it is returned, otherwise a new {@link Array} is stored in the cache and returned. All returned arrays are
* frozen (readonly). */
getArray(values) {
const array = values.map((v) => this.getValue(v));
return __privateMethod(this, _ValueObjectCache_instances, get_fn).call(this, array.constructor, array, () => Object.freeze(array));
}
getValue(value) {
if (isPrimitive(value)) {
return value;
} else if (Array.isArray(value)) {
return this.getArray(value);
} else if (isValueObject(value)) {
return value;
} else {
throw new TypeError("Invalid value type: a value must be a primitive, an array, or a ValueObject.");
}
}
}, _rootNode = new WeakMap(), _finalizationRegistry = new WeakMap(), _ValueObjectCache_instances = new WeakSet(), get_fn = function(constructor, values, factory) {
var _a3;
const path = [constructor, ...values];
let node = __privateGet(this, _rootNode);
for (const key of path) {
if (!node.children) node.children = /* @__PURE__ */ new Map();
let childNode = node.children.get(key);
if (!childNode) {
childNode = { children: null, instanceRef: null };
node.children.set(key, childNode);
}
node = childNode;
}
const cachedInstance = (_a3 = node.instanceRef) == null ? void 0 : _a3.deref();
if (cachedInstance) return cachedInstance;
const instance = factory();
if (instance.constructor !== constructor) {
throw new TypeError("factory must return an instance of the provided constructor");
}
node.instanceRef = new WeakRef(instance);
__privateGet(this, _finalizationRegistry).register(instance, path);
return instance;
}, _a2)();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ValueObject,
isPrimitive,
isValue,
isValueArray,
isValueObject,
valueObjectCache
});
//# sourceMappingURL=index.js.map