+/**
+ * An object whose properties are lazy‐loaded from the methods on the
+ * own properties of the provided object.
+ *
+ * This is useful when you are looking to reference properties on
+ * objects which, due to module dependency graphs, cannot be guaranteed
+ * to have been initialized yet.
+ *
+ * The resulting properties will have the same attributes (regarding
+ * configurability, enumerability, and writability) as the
+ * corresponding properties on the methods object. If a property is
+ * marked as writable, the method will never be called if it is set
+ * before it is gotten. By necessity, the resulting properties are all
+ * configurable before they are accessed for the first time.
+ *
+ * Methods will be called with the resulting object as their this
+ * value.
+ *
+ * LazyLoader objects have the same prototype as the passed methods
+ * object.
+ */
+export class LazyLoader extends null {
+ /**
+ * Constructs a new LazyLoader object.
+ *
+ * ☡ This function throws if the provided value is not an object.
+ */
+ constructor(loadMethods) {
+ if (type(loadMethods) !== "object") {
+ throw new TypeError(
+ `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
+ );
+ } else {
+ const result = objectCreate(getPrototype(loadMethods));
+ const methodKeys = getOwnPropertyKeys(loadMethods);
+ for (let index = 0; index < methodKeys.length; ++index) {
+ const methodKey = methodKeys[index];
+ const { configurable, enumerable, writable } =
+ getOwnPropertyDescriptor(loadMethods, methodKey);
+ defineOwnProperty(result, methodKey, {
+ configurable: true,
+ enumerable,
+ get: () => {
+ const value = call(loadMethods[methodKey], result, []);
+ defineOwnProperty(result, methodKey, {
+ configurable,
+ enumerable,
+ value,
+ writable,
+ });
+ return value;
+ },
+ set: writable
+ ? ($) =>
+ defineOwnProperty(result, methodKey, {
+ configurable,
+ enumerable,
+ value: $,
+ writable,
+ })
+ : void {},
+ });
+ }
+ return result;
+ }
+ }
+}
+