proxify.js

"use strict";

const proxyMap = new WeakMap();

const target = Object.setPrototypeOf(() => {}, null);

delete target.name;
delete target.length;

Object.freeze(target);

/**
 * A proxy for an object that has a Set for this property key will pass through all properties with a name that can be found in that Set.
 * Used internally to define the properties of Apis that should be passed through (i. e. "then" and "catch").
 * @memberof module:proxify
 * @private
 * @ignore
 * @type symbol
 */
const passthrough = Symbol("passthrough");

/**
 * Returns a proxy for the given {@link Api} or {@link ClientApi}. Accessing a property `A` of it will return `proxify(api.route(A))`. Calling the proxy with an argument `B` will return `api.close(B)`.
 * When the properties `then` and `catch` of the proxy are accessed they will not be translated into `route` calls but passed through to `api` directly instead.
 * @module proxify
 * @param {Api|ClientApi} api The Api to proxify.
 * @return {Proxy} A proxified version of the given Api.
 */
function proxify(api) {
	if(!api || typeof api !== "object")
		throw new TypeError("Only objects can be proxified.");

	if(typeof api.route !== "function" || typeof api.close !== "function")
		throw new TypeError("Only objects that offer an owe Api interface can be proxified.");

	const passthroughSet = api[passthrough];

	const proxy = new Proxy(target, {
		get(target, property) {
			if(typeof property === "symbol" || passthroughSet && passthroughSet.has(property) && property in api)
				return typeof api[property] === "function" ? api[property].bind(api) : api[property];

			return proxify(api.route(property));
		},

		apply(target, context, args) {
			return api.close(args[0]);
		},

		deleteProperty() {
			return false;
		}
	});

	proxyMap.set(proxy, api);

	return proxy;
}

/**
 * Reverts the proxify function.
 * @memberof module:proxify
 * @param {Proxy} proxy A proxy that was returned by {@link proxify}.
 * @return {Api|ClientApi} The Api that was used to construct `proxy`.
 */
function revert(proxy) {
	return proxyMap.get(proxy);
}

module.exports = Object.assign(proxify, { revert, passthrough });