123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- /**
- * Module dependencies
- */
- var util = require('util');
- var path = require('path');
- var _ = require('@sailshq/lodash');
- var flaverr = require('flaverr');
- var getIsProductionWithoutDebug = require('./private/get-is-production-without-debug');
- var hashCustomUsageOpts = require('./private/hash-custom-usage-opts');
- var helpBuildMachine = require('./private/help-build-machine');
- var getMethodName = require('./get-method-name');
- /**
- * .pack()
- *
- * Build a Pack, either from the specified Node-Machine definitions or by loading modules
- * from the specified directory.
- *
- * > This method is used by default in the index.js file of generated machinepacks.
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @required {String|Dictionary?} options
- * Either the absolute path to the location of the modules to load & pack (see `dir` below)
- * -OR- a dictionary of options:
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @property {Dictionary?} defs
- * An alternative to `dir` and `pkg`. If specified, this is a dictionary
- * of raw definitions, keyed by identity. (This makes auto-require irrelevant.)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @property {String?} name
- * If unspecified, `pkg.name` will be used.
- *
- * @property {String?} version
- * If unspecified, `pkg.version` will be used.
- *
- * @property {String?} description
- * If unspecified, `pkg.description` will be used.
- *
- * @property {String?} license
- * If unspecified, `pkg.license` will be used.
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @property {String?} dir
- * The absolute path to the location of the modules to load & pack.
- * (If a relative path is specified, it will be resolved relative from the `pkg`)
- *
- * @property {Dictionary?} pkg
- * The package dictionary (i.e. what package.json exports).
- * Will be used for refining the directory to load modules from.
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @property {Dictionary?} customize
- * ** THIS IS EFFECTIVELY PRIVATE, AND NOT DESIGNED FOR EXTERNAL USE! **
- * Custom usage options to apply to every Callable that is auto-built by .pack().
- * (This is a key part of `.pack()`'s recursive call to itself. This isn't necessarily
- * how these things are set from userland though-- the best way to do that is with
- * the .customize() method.)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * @returns {Pack}
- * A Pack instance- basically a dictionary of Callables ("wet machines") with
- * camelCased keys ("method names"). Plus a few extra methods.
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Example usage:
- * ```
- * foo = machine.pack({
- * name: 'Foo',
- * defs: {
- * 'do-something': {
- * implementationType: 'classical'
- * inputs: {
- * favoriteColor: { type: 'string', required: true },
- * },
- * fn: async function(){
- * console.log('did it');
- * }
- * }
- * }
- * })
- * .customize({
- * arginStyle: 'serial',
- * execStyle: 'immediate'
- * });
- *
- * await foo.doSomething('quickly');
- * ```
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- */
- module.exports = function pack(optionsOrDir){
- // Normalize usage to determine actual options.
- // > e.g. if specified as a string, understand as `options.dir`, etc.
- var options;
- if (_.isString(optionsOrDir)) {
- options = { dir: optionsOrDir };
- } else if (optionsOrDir === undefined) {
- options = {};
- } else if (!_.isObject(optionsOrDir) || _.isFunction(optionsOrDir) || _.isArray(optionsOrDir)) {
- throw new Error('Usage error: `.pack()` expects a dictionary of options, but instead got:'+util.inspect(optionsOrDir, {depth:null}));
- } else {
- options = optionsOrDir;
- }
- // Usage sanity checks:
- if (options.pkg !== undefined) {
- if (!_.isObject(options.pkg) || _.isArray(options.pkg) || _.isFunction(options.pkg)) {
- throw new Error('Usage error: `.pack()` received an invalid `pkg`. If specified, `pkg` must be a dictionary, but instead got:'+util.inspect(options.pkg, {depth:null}));
- }
- }//fi
- if (options.dir !== undefined) {
- if (!_.isString(options.dir)) {
- throw new Error('Usage error: `.pack()` received a `dir` path which is not a string:'+util.inspect(options.dir, {depth:null}));
- }
- }//fi
- // Local variables to provide convenient access below.
- // Uses either direct key (`options.*`) or nested under pkg (`options.pkg.*`).
- var name = options.name !== undefined ? options.name : (options.pkg&&options.pkg.name);
- var version = options.version !== undefined ? options.version : (options.pkg&&options.pkg.version);
- var description = options.description !== undefined ? options.description : (options.pkg&&options.pkg.description);
- var license = options.license !== undefined ? options.license : (options.pkg&&options.pkg.license);
- // Use defs if any were provided.
- var defs = options.defs;
- // █████╗ ██╗ ██╗████████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗██╗██████╗ ███████╗
- // ██╔══██╗██║ ██║╚══██╔══╝██╔═══██╗ ██╔══██╗██╔════╝██╔═══██╗██║ ██║██║██╔══██╗██╔════╝
- // ███████║██║ ██║ ██║ ██║ ██║█████╗██████╔╝█████╗ ██║ ██║██║ ██║██║██████╔╝█████╗
- // ██╔══██║██║ ██║ ██║ ██║ ██║╚════╝██╔══██╗██╔══╝ ██║▄▄ ██║██║ ██║██║██╔══██╗██╔══╝
- // ██║ ██║╚██████╔╝ ██║ ╚██████╔╝ ██║ ██║███████╗╚██████╔╝╚██████╔╝██║██║ ██║███████╗
- // ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝
- //
- // If no defs were explicitly provided, then auto-require modules.
- if (defs === undefined) {
- // Make sure `dir` was specified.
- if (!options.dir) {
- throw flaverr({
- name: 'UsageError',
- message:
- 'Failed to .pack(). Since no `defs` were provided, tried to auto-load modules, but cannot.\n'+
- 'Please specify a `dir` (usually: `dir: __dirname`)'
- });
- }//•
- var explicitIdentitiesToLoad;
- if (options.pkg) {
- if (options.pkg&&options.pkg.machinepack&&options.pkg.machinepack.machines&&_.isArray(options.pkg.machinepack.machines)){
- explicitIdentitiesToLoad = options.pkg.machinepack.machines;
- } else {
- throw flaverr({
- name: 'ImplementationError', // formerly: {code: 'E_INVALID_OPTION'}
- message:
- 'Failed to .pack(). Provided `pkg.machinepack.machines` is invalid. '+
- '(Should be an array of strings.)'
- });
- }
- }//fi
- // If no explicit identities were provided, just try to load all `.js` files in `dir`.
- // (with the exception of `index.js`, which is reserved.)
- if (!explicitIdentitiesToLoad) {
- throw new Error('Invalid options: If `defs` is not provided, then both `dir` AND `pkg` must be set.');
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // FUTURE: MAYBE bring the following naive "load everything" approach back
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // // // Ensure we have an absolute path.
- // // options.dir = path.resolve(options.dir);
- // // // Load modules (as dry machine definitions)
- // // var inventory = includeAll({
- // // dirname: options.dir,
- // // filter: /(.+)\.js/,
- // // exclude: [
- // // /^index.js$/
- // // ],
- // // flatten: true
- // // });
- // // defs = _.reduce(inventory, function (memo, rawNMDef, key) {
- // // rawNMDef.identity = _.kebabCase(key);
- // // memo[rawNMDef.identity] = rawNMDef;
- // // return memo;
- // // }, {});
- // // </naive "load all">
- // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- } else {
- // Resolve source directory path from `machineDir` option.
- // > Note that we tolerate mispellings and absence of the option, for backwards compat.
- var srcDir = options.pkg.machinepack.machineDir || options.pkg.machinepack.machinedir || '';
- if (!_.isString(srcDir)) {
- throw flaverr({
- name: 'ImplementationError',
- message:
- 'Provided `pkg.machinepack.machineDir` is invalid.\n'+
- 'If specified, should be a string-- the relative path to the directory\n'+
- 'where module definitions will be loaded from (e.g. "./lib" or "./machines").'
- });
- }//•
- // Load defs.
- defs = _.reduce(explicitIdentitiesToLoad, function (defs, identityToLoad) {
- var pathToRequire = path.resolve(options.dir, srcDir, identityToLoad);
- var def;
- try {
- def = require(pathToRequire);
- } catch (err) {
- if (flaverr.taste('MODULE_NOT_FOUND', err)) {
- throw flaverr({
- name: 'ImplementationError',
- code: 'MODULE_NOT_FOUND',// formerly: 'E_MACHINE_NOT_FOUND'
- raw: err,
- message:
- 'Could not find `'+identityToLoad+'`, one of the modules in `pkg.machinepack.machines`.\n'+
- 'Maybe it was deleted or mistyped?',
- });
- } else {
- throw flaverr({
- name: 'ImplementationError',
- code: 'E_INVALID_DEF',// formerly: 'E_INVALID_MACHINE'
- raw: err,
- message: 'Error loading `'+identityToLoad+'`, one of the modules in `pkg.machinepack.machines`.'
- });
- }
- }//>-
- defs[identityToLoad] = def;
- return defs;
- }, {});//∞
- }//fi (loaded using explicit identities)
- }//fi (had to auto-require)
- // ██╗███╗ ██╗███████╗████████╗ █████╗ ███╗ ██╗████████╗██╗ █████╗ ████████╗███████╗
- // ██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗████╗ ██║╚══██╔══╝██║██╔══██╗╚══██╔══╝██╔════╝
- // ██║██╔██╗ ██║███████╗ ██║ ███████║██╔██╗ ██║ ██║ ██║███████║ ██║ █████╗
- // ██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║╚██╗██║ ██║ ██║██╔══██║ ██║ ██╔══╝
- // ██║██║ ╚████║███████║ ██║ ██║ ██║██║ ╚████║ ██║ ██║██║ ██║ ██║ ███████╗
- // ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
- //
- // ██████╗ █████╗ ██████╗██╗ ██╗
- // ██╔══██╗██╔══██╗██╔════╝██║ ██╔╝
- // ██████╔╝███████║██║ █████╔╝
- // ██╔═══╝ ██╔══██║██║ ██╔═██╗
- // ██║ ██║ ██║╚██████╗██║ ██╗
- // ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
- //
- // Now set up the dictionary that hold our machines.
- var newPack = {};
- // Set up a place to cache customized sub-packs.
- // (See `customize()` below for more information.)
- var cachedCustomSubPacks = {};
- // Set up a home for default argins for this pack.
- // (See `configure()` below for more information.)
- //
- // Remember: These are NOT PROCESS-WIDE!
- // (Just pack-instance-wide)
- var defaultArgins = {};
- /**
- * .customize()
- *
- * Build a customized version of this pack, with machines re-built to work
- * using the specified custom usage options.
- * > If this exact customization has been used before for this pack,
- * > the customized pack will be _cloned and cached_. This works much
- * > like Node's core require() cache, and is designed to improve performance
- * > by avoiding unnecessarily duplicating work on a per-call basis.
- *
- * @param {Dictionary} customUsageOpts
- * @property {String?} arginStyle ("named" or "serial")
- * @property {String?} execStyle ("deferred" or "immediate")
- * … (For full reference of opts, see `buildWithCustomUsage()`)
- *
- * @returns {Ref} [a custom, spin-off duplicate of this pack w/ custom usage]
- */
- Object.defineProperty(newPack, 'customize', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function customize(customUsageOpts){
- if (!getIsProductionWithoutDebug()) {
- if (!customUsageOpts || _.isEqual(customUsageOpts, {})) { throw new Error('Consistency violation: Cannot call `.customize()` without providing any custom usage options! Please specify at least one option such as "arginStyle" or "execStyle".'); }
- if (!_.isObject(customUsageOpts) || _.isArray(customUsageOpts) || _.isFunction(customUsageOpts)) { throw new Error('Consistency violation: `.customize()` must be called with a dictionary of custom usage options.'); }
- if (customUsageOpts.def !== undefined) { throw new Error('Consistency violation: Cannot specify `def` when calling `.customize()` on a package! Instead provide options like "arginStyle" or "execStyle".'); }
- }//fi
- var hashed;
- try {
- // - - - - - - - - - - - - - - - - - - - - - - - -
- // FUTURE: Simplify away this try/catch block with something like:
- // ```
- // var hashCustomUsageOpts = parley.callable(require('./private/hash-custom-usage-opts'));
- // hash = hashCustomUsageOpts(customUsageOpts).tolerate('E_UNHASHABLE').now();
- // ```
- // Or potentially: («« although this approach is slower-- plus it's more convoluted...)
- // ```
- // hash = parley.deferred(hashCustomUsageOpts, undefined, [customUsageOpts]).tolerate('E_UNHASHABLE').now();
- // ```
- // - - - - - - - - - - - - - - - - - - - - - - - -
- hashed = hashCustomUsageOpts(customUsageOpts);
- } catch (err) {
- if (flaverr.taste('E_UNHASHABLE', err)) {
- // Just do nothing else and continue
- // (but leave `hashed` falsy)
- // - - - - - - - - - - - - - - - - - - - - - - - -
- // Note, we could also log a warning like
- // ```
- // console.warn('WARNING: Could not compute hash from provided custom usage options. (Building customization of pack without caching for now...but this will probably be slow at runtime!)');
- // ```
- // …but then again it's perfectly valid to do this,
- // and you can always cache this yourself in userland
- // if it's slow. So that's why we didn't use this
- // warning.
- // - - - - - - - - - - - - - - - - - - - - - - - -
- } else {
- throw err;
- }
- }//>-
- // Use cached customization, if possible.
- if (hashed && cachedCustomSubPacks[hashed]) {
- return cachedCustomSubPacks[hashed];
- }//-•
- // Prepare a custom sub-pack
- // (this makes a recursive call to .pack() to ensure things get expanded
- // consistently, and that we get all of the goodies like `.inspect()`,
- // `.toString()`, etc.)
- var thisPack = this;
- var customSubPack = pack({
- name: name,
- version: version,
- description: description,
- license: license,
- customize: customUsageOpts,
- defs: _.reduce(_.keys(defs), function(_shallowCopyOfDryDefs, identity) {
- var callable = thisPack[getMethodName(identity)];
- var def = callable.getDef();
- _shallowCopyOfDryDefs[def.identity] = def;
- return _shallowCopyOfDryDefs;
- }, {})
- });
- // Apply default argins (if there are any) to this custom sub-pack
- if (_.keys(defaultArgins).length > 0) {
- customSubPack.configure(defaultArgins);
- }
- // If possible, cache this customization to speed up the next time this
- // variation of `.customize()` gets called again.
- if (hashed) {
- cachedCustomSubPacks[hashed] = customSubPack;
- }
- return customSubPack;
- }//ƒ
- });//…)
- /**
- * .configure()
- *
- * Set default argins for this pack.
- *
- * Note that these are NOT PROCESS-WIDE!
- * (Just pack-instance-wide)
- *
- * (Also note that, if called more than once, the subsequent call shallow-extends
- * any previous default argins that were configured. However, if the new default
- * argins have a key with an undefined value, that key will be ignored.)
- */
- Object.defineProperty(newPack, 'configure', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function configure(_newDefaultArgins){
- // Strip keys w/ `undefined` as their value, for consistency.
- _.each(_.keys(_newDefaultArgins), function(inputCodeName){
- if (_newDefaultArgins[inputCodeName] === undefined) {
- delete _newDefaultArgins[inputCodeName];
- }
- });//∞
- // Then just do a normal, shallow extend.
- _.extend(defaultArgins, _newDefaultArgins);
- // Chainable
- return newPack;
- }
- });
- /**
- * .inspect()
- *
- * (Automatically invoked in Node.js environments when this is passed into `util.inspect()` or `console.log()`)
- *
- * > This property can be overridden.
- */
- Object.defineProperty(newPack, 'inspect', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function inspect(){
- return ''+
- '-----------------------------------------\n'+
- ' '+(name || 'anonymous package')+'\n'+
- (version ? ' v'+version : '')+(license ? ' ('+license+')\n' : version?'\n':'')+
- // (name ? ' (http://npmjs.com/package/'+name+')\n' : '')+
- ' \n'+
- (description ?
- (' '+description+'\n\n') :
- ''
- )+
- ' Methods:\n'+
- (
- _.map(_.keys(defs).sort(), function (nmIdentity){
- return ' · '+getMethodName(nmIdentity)+'()';
- }).join('\n')||
- ' (n/a)'
- )+'\n'+
- '-----------------------------------------\n';
- }//ƒ
- });//…)
- /**
- * .toString()
- *
- * (Automatically invoked before casting, string concatenation, etc.)
- *
- * > This property can be overridden.
- */
- Object.defineProperty(newPack, 'toString', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function toString(){
- return '[Package: '+(name||'anonymous')+']';
- }//ƒ
- });
- /**
- * .toJSON()
- *
- * Get a dry, JSON-compatible representation of this pack.
- *
- * Note that, if this "dry" pack representation is ACTUALLY JSON-stringified afterwards,
- * the stringification process will be lossy. Functions like `fn` or `custom` validators
- * are not actually JSON serializable. (To overcome this, use an additional layer of
- * serialization and deserialization such as rttc.dehydrate() and rttc.hydrate().)
- *
- * (Automatically invoked before JSON stringification when this is passed
- * into `JSON.stringify()`)
- */
- Object.defineProperty(newPack, 'toJSON', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function toJSON(){
- var thisPack = this;
- return {
- name: name||'anonymous',
- version: version||'',
- description: description||'',
- license: license||'',
- customize: options.customize||undefined,
- defs: _.reduce(_.keys(defs), function(_shallowCopyOfDryDefs, identity) {
- var callable = thisPack[getMethodName(identity)];
- var def = callable.getDef();
- _shallowCopyOfDryDefs[def.identity] = def;
- return _shallowCopyOfDryDefs;
- }, {})
- };
- }//ƒ
- });
- /**
- * .registerDefs()
- *
- * Attach a dictionary of new methods, keyed by identity.
- * Any existing properties with the same names will be overridden.
- *
- * > Note that this method must be used in order for new methods
- * > to work properly! Anything NOT attached this way but added
- * > as a property WILL be allowed (e.g. sub-packs), but it will
- * > be ignored as far as methods are concerned.
- *
- * @param {Dictionary} newMethodsByIdentity
- */
- Object.defineProperty(newPack, 'registerDefs', {
- enumerable: false,
- configurable: false,
- writable: true,
- value: function registerDefs(newMethodsByIdentity){
- // Attach new methods to `defs`.
- _.extend(defs, newMethodsByIdentity);
- // Build up a constant array of unconventional method names
- // (used below to show a warning if a machine identity looks too similar to native JS or Node stuff.)
- //
- // FUTURE: Merge in the rest from parley too
- var UNCONVENTIONAL_OR_RESERVED_METHOD_NAMES = [
- // In general:
- 'inspect', 'toString', 'valueOf', 'toLocaleString', 'toJSON',
- 'prototype', 'constructor',
- 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
- // To watch out for with .pack() specifically:
- 'customize', 'configure', 'registerDefs'
- ];
- // Attach methods by building defs into callables.
- // (Along the way, set identities and auto-infer method names.)
- _.each(defs, function(def, identity){
- if (def.identity !== undefined && def.identity !== identity) {
- throw flaverr({
- name: 'UsageError',
- message: 'The definition under key "'+identity+'" has an inconsistent `identity` property: '+def.identity
- });
- }//•
- if (_.contains(UNCONVENTIONAL_OR_RESERVED_METHOD_NAMES, getMethodName(identity))) {
- console.warn('WARNING: `'+identity+'` is an unconventional or reserved identity. When converted to a method name (`'+getMethodName(identity)+'`), it could conflict with native features of JavaScript/Node.js, or otherwise interfere with native features of this runner. Please use a different identity instead. (Proceeding anyway this time...)');
- }
- var callable = helpBuildMachine(_.extend({}, options.customize||{}, {
- def: _.extend({ identity: identity }, def),
- defaultArgins: defaultArgins
- }));
- newPack[getMethodName(identity)] = callable;
- });//∞
- }//ƒ
- });//…)
- // ██████╗ ██╗ ██╗██╗██╗ ██████╗
- // ██╔══██╗██║ ██║██║██║ ██╔══██╗
- // ██████╔╝██║ ██║██║██║ ██║ ██║
- // ██╔══██╗██║ ██║██║██║ ██║ ██║
- // ██████╔╝╚██████╔╝██║███████╗██████╔╝
- // ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝
- //
- // ██╗███╗ ███╗██████╗ ██╗ ███████╗███╗ ███╗███████╗███╗ ██╗████████╗███████╗██████╗
- // ██║████╗ ████║██╔══██╗██║ ██╔════╝████╗ ████║██╔════╝████╗ ██║╚══██╔══╝██╔════╝██╔══██╗
- // ██║██╔████╔██║██████╔╝██║ █████╗ ██╔████╔██║█████╗ ██╔██╗ ██║ ██║ █████╗ ██║ ██║
- // ██║██║╚██╔╝██║██╔═══╝ ██║ ██╔══╝ ██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║ ██╔══╝ ██║ ██║
- // ██║██║ ╚═╝ ██║██║ ███████╗███████╗██║ ╚═╝ ██║███████╗██║ ╚████║ ██║ ███████╗██████╔╝
- // ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═════╝
- //
- // ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗ ███████╗
- // ████╗ ████║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗██╔════╝
- // ██╔████╔██║█████╗ ██║ ███████║██║ ██║██║ ██║███████╗
- // ██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║╚════██║
- // ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚██████╔╝██████╔╝███████║
- // ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
- //
- newPack.registerDefs(defs);
- return newPack;
- };
|