UNPKG

@homer0/jimple

Version:

An extended version of the Jimple lib, with extra features

481 lines (471 loc) 17.8 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // ../../../node_modules/.pnpm/jimple@1.5.0/node_modules/jimple/src/Jimple.js var require_Jimple = __commonJS({ "../../../node_modules/.pnpm/jimple@1.5.0/node_modules/jimple/src/Jimple.js"(exports, module) { "use strict"; function assert(ok, message) { if (!ok) { throw new Error(message); } } function isFunction(fn) { return Object.prototype.toString.call(fn) === "[object Function]" && fn.constructor.name === "Function"; } function isPlainObject(value) { if (Object.prototype.toString.call(value) !== "[object Object]") { return false; } let prototype = Object.getPrototypeOf(value); return prototype === null || prototype === Object.prototype; } function checkDefined(container, key) { assert(container.has(key), `Identifier "${key}" is not defined.`); } function addFunctionTo(set, fn) { assert(isFunction(fn), "Service definition is not a Closure or invokable object"); set.add(fn); return fn; } var Jimple3 = class { static provider(register) { return { register }; } /** * Create a Jimple Container. * @param {Object?} [values] - An optional object whose keys and values will be associated in the container at initialization */ constructor(values) { this._items = {}; this._instances = /* @__PURE__ */ new Map(); this._factories = /* @__PURE__ */ new Set(); this._protected = /* @__PURE__ */ new Set(); values = isPlainObject(values) ? values : {}; Object.keys(values).forEach(function(key) { this.set(key, values[key]); }, this); } /** * Return the specified parameter or service. If the service is not built yet, this function will construct the service * injecting all the dependencies needed in it's definition so the returned object is totally usable on the fly. * @param {string} key - The key of the parameter or service to return * @return {*} The object related to the service or the value of the parameter associated with the key informed * @throws If the key does not exist */ get(key) { checkDefined(this, key); let item = this._items[key]; let obj; if (isFunction(item)) { if (this._protected.has(item)) { obj = item; } else if (this._instances.has(item)) { obj = this._instances.get(item); } else { obj = item(this); if (!this._factories.has(item)) { this._instances.set(item, obj); } } } else { obj = item; } return obj; } /** * Defines a new parameter or service. * @param {string} key - The key of the parameter or service to be defined * @param {*} value - The value of the parameter or a function that receives the container as parameter and constructs the service */ set(key, value) { this._items[key] = value; } /** * Returns if a service or parameter with the informed key is already defined in the container. * @param {string} key - The key of the parameter or service to be checked. * @return {boolean} If the key exists in the container or not */ has(key) { return this._items.hasOwnProperty(key); } /** * Defines a service as a factory, so the instances are not cached in the service and that function is always called * @param {function(Jimple):*} fn - The function that constructs the service that is a factory * @return {function(Jimple):*} The same function passed as parameter */ factory(fn) { return addFunctionTo(this._factories, fn); } /** * Defines a function as a parameter, so that function is not considered a service * @param {*} fn - The function to not be considered a service * @return {*} The same function passed as parameter */ protect(fn) { return addFunctionTo(this._protected, fn); } /** * Return all the keys registered in the container * @returns {Array<string>} */ keys() { return Object.keys(this._items); } /** * Extends a service already registered in the container * @param {string} key - The key of the service to be extended * @param {function(*, Jimple):*} fn - The function that will be used to extend the service * @throws If the key is not already defined * @throws If the key saved in the container does not correspond to a service * @throws If the function passed it not...well, a function */ extend(key, fn) { checkDefined(this, key); let originalItem = this._items[key]; assert(isFunction(originalItem) && this._protected.has(originalItem) === false, `Identifier '${key}' does not contain a service definition`); assert(isFunction(fn), `The 'new' service definition for '${key}' is not a invokable object.`); this._items[key] = function(app) { return fn(originalItem(app), app); }; if (this._factories.has(originalItem)) { this._factories.delete(originalItem); this._factories.add(this._items[key]); } } /** * Uses an provider to extend the service, so it's easy to split the service and parameter definitions across the system * @param {{register: function(Jimple)}} provider - The provider to be used to register services and parameters in this container */ register(provider2) { provider2.register(this); } /** * Returns the raw value of a service or parameter. So, in the case of a service, for example, the value returned is the * function used to construct the service. * @param {string} key - The key of the service or parameter to return. * @throws If the key does not exist in the container * @return {*} The raw value of the service or parameter */ raw(key) { checkDefined(this, key); return this._items[key]; } }; module.exports = Jimple3; } }); // src/jimplemod/index.ts var import_jimple = __toESM(require_Jimple()); var Jimple = import_jimple.default; // src/jimple/index.ts var Jimple2 = class extends Jimple { /** * This is a version of the `get` method that doesn't throw if the key doesn't exist. It * will return `undefined` instead. * * @param key The key of the parameter or service to return. * @returns The object related to the service or the value of the parameter * associated with the given key. * @template T The type of the returned object. */ try(key) { if (!this.has(key)) { return void 0; } return this.get(key); } }; var jimple = (...args) => new Jimple2(...args); // src/factories/resourceFactory.ts var resourceFactory = () => ( /** * Generates a new resource using the contraint defined in the factory. * * @param name The name of the resource. * @param key The property key for the function. * @param fn The resource function. * @returns An object with the `name` set to `true`, and the `fn` as the `key`. * @template Name The literal type of `name`, to be used in the return object. * @template Key The literal type of `key`, to be used in the return object. * @template Fn The type of `fn`, restricted by the factory constraint. * @example * * const myAction = factory('action', 'fn', (c) => { * c.set('foo', 'bar'); * }); * */ (name, key, fn) => ({ [name]: true, [key]: fn }) ); // src/factories/resourceCreatorFactory.ts var resourceCreatorFactory = () => ( /** * Generates a new resource creator using the contraint defined in the factory. * * @param name The name of the resource. * @param key The property key for the function. * @param creatorFn The curried function that returns the actual resource function. * @returns A resource creator: an object that can be used as a resource, and as a * function that returns a resource. * @template Name The literal type of `name`, to be used in the return object. * @template Key The literal type of `key`, to be used in the return object. * @template ResFn The type of the resource function, the creator function needs * to return. This is restricted by the factory constraint. * @template CreatorFn The literal type of `creatorFn`, to be used in the return * object. * @example * * const myAction = factory('action', 'fn', (name = 'foo') => (c) => { * c.set(name, 'bar'); * }); * */ (name, key, creatorFn) => { const fnToProxy = (...args) => { const actualResource = creatorFn(...args); return resourceFactory()(name, key, actualResource); }; const handler = { name, resource: null, get(target, property) { let result; if (property === this.name) { result = true; } else if (property === key) { if (this.resource === null) { const newResource = creatorFn(); this.resource = newResource; } result = this.resource; } else { const targetKey = property; result = target[targetKey]; } return result; }, has(target, property) { if (property === this.name || property === key) { return true; } const targetKey = property; return targetKey in target; } }; return new Proxy(fnToProxy, handler); } ); // src/factories/resourcesCollectionFactory.ts var resourcesCollectionFactory = () => ( /** * Generates a function to create a collection of resources of an specified type. A * collection is a dictionary of resources, and a resource itself, and when its * "resource function" gets called, it calls the function of every resource (with the * same args it recevied). * * @param name The resource name the items in the collection must have. * @param key The key property the items in the collection must have. * @param fn A custom function to process the items in the collection, when the * collection function gets called. * @returns A function that can be used to create a resources collection. * @template Name The literal type of `name`, to be used in the return object. * @template Key The literal type of `key`, to be used in the return object. * @template ItemKey To capture the name of the resources in the collection. * @template Items The kind of dictionary of resources the return function will * expect. * @template CustomFn The type of `fn`, restricted by the factory constraint. * @example * * const myActions = factory('action', 'fn')({ myActionA, myActionB }); * */ (name, key, fn) => { const collectionResourceFactory = resourceFactory(); return (items) => { const invalidKeys = [name, key]; const itemsKeys = Object.keys(items); const invalidKey = itemsKeys.some( (itemKey) => invalidKeys.includes(String(itemKey)) ); if (invalidKey) { throw new Error( `No item on the collection can have the keys \`${name}\` nor \`${key}\`` ); } const invalidItem = itemsKeys.find( (itemKey) => typeof items[itemKey]?.[key] !== "function" ); if (invalidItem) { throw new Error( `The item \`${String( invalidItem )}\` is invalid: it doesn't have a \`${key}\` function` ); } const useFn = fn ? (...args) => fn(items, ...args) : (...args) => { itemsKeys.forEach((itemK) => { const item = items[itemK]; const itemFn = item[key]; itemFn(...args); }); }; return { ...collectionResourceFactory(name, key, useFn), ...items }; }; } ); // src/helpers/injectHelper.ts var InjectHelper = class { /** * This method is meant to be used to validate the dependencies a service receives, and * needs. * It will check on the received dependencies, if a specific dependency exists, it will * return it, otherwise, it will create a new instance. * * @param dependencies The dependencies received by the implementation. * @param key The key of the dependency to validate. * @param init A function to create a dependency in case it doesn't exist in * the dictionary. * @returns An instance of the dependency. * @template DepKey The literal key of the dependency to validate. * @template DepType The type of the dependency, obtained from the dictionary sent * to the constructor. * @example * * type Dependencies = { * dep: string; * }; * const helper = new InjectHelper<Dependencies>(); * * type MyServiceOptions = { * inject?: Partial<Dependencies>; * }; * const myService = ({ inject = {} }: MyServiceOptions) => { * const dep = helper.get(inject, 'dep', () => 'default'); * console.log('dep:', dep); * }; * */ get(dependencies, key, init) { const existing = dependencies[key]; if (typeof existing !== "undefined") { return existing; } return init(); } /** * This is meant to be used in a provider creator to resolve dependencies' names sent as * options. For each dependency, the method will first check if a new name was * specified, and try to get it with that name, otherwise, it will try to get it with * the default name, and if it doesn't exist, it will just keep it as `undefined` and * expect the service implements {@link InjectHelper.get} to ensure the dependency. * * @param dependencies The dependencies needed by the service. * @param container A reference to the Jimple container. * @param inject A dictionary of dependencies names. * @returns A dictionary of dependencies to send to the service. * @template DepKey The literal key of the dependencies to validate. * @template Container The type of the Jimple container. * @example * * type Dependencies = { * dep: string; * }; * const helper = new InjectHelper<Dependencies>(); * * type MyProviderOptions = { * services?: { * [key in keyof Dependencies]?: string; * }; * }; * * const myProvider = providerCreator( * ({ services = {} }: MyProviderOptions) => * (container) => { * container.set('myService', () => { * const inject = helper.resolve(['dep'], container, services); * return myService({ inject }); * }); * }, * ); * */ resolve(dependencies, container, inject) { const result = dependencies.reduce((acc, key) => { if (inject[key]) { acc[key] = container.get(inject[key]); return acc; } const keyStr = key; if (container.has(keyStr)) { acc[key] = container.get(keyStr); return acc; } return acc; }, {}); return result; } }; var injectHelper = () => new InjectHelper(); // src/helpers/provider.ts var createProvider = () => { const factory = resourceFactory(); return (register) => factory("provider", "register", register); }; var provider = createProvider(); // src/helpers/providerCreator.ts var createProviderCreator = () => { const factory = resourceCreatorFactory(); return (creator) => factory("provider", "register", creator); }; var providerCreator = createProviderCreator(); // src/helpers/providers.ts var createProviders = () => { const factory = resourcesCollectionFactory(); return factory("provider", "register"); }; var providers = createProviders(); export { InjectHelper, Jimple2 as Jimple, createProvider, createProviderCreator, createProviders, injectHelper, jimple, provider, providerCreator, providers, resourceCreatorFactory, resourceFactory, resourcesCollectionFactory }; //# sourceMappingURL=index.js.map