<commit_before>/*jshint esnext:true*/

/**
 * @param	{Object} target	object to receive mixins
 * @param	{Object[]} mixins
 * @return	{Object}
 */
export function mix (target, ...mixins) {
	if (!target.hasOwnProperty('__mixins')) {
		target.__mixins = [];
	}
	
	for (let mixin of mixins) {
		// register mixin
		if (target.__mixins.indexOf(mixin) !== -1) {
			continue;	// already mixed in
		}
		target.__mixins.push(mixin);
		
		// copy properties
		for (let prop of Object.keys(mixin)) {
			if (!target.hasOwnProperty(prop) && prop !== 'constructor') {
				target[prop] = mixin[prop];
			}
		}
	}
	
	return target;
}

/**
 * 
 * @param {Object} prototype
 * @returns {Object}
 */
export function factoryFactory (prototype = Object.prototype) {
	let factory = Object.create(prototype);
	
	factory.create = function (...args) {
		let ob = Object.create(factory);
		
		prototype.init.apply(ob, args);
		
		return ob;
	};
	
	return factory;
}

const initMap = new Map();	// cache factory => (next) init

function findFactoryWithNextInit (factory) {
	if (factory.init) {
		return factory;
	}
	
	if (factory.super) {
		return findFactoryWithNextInit(factory.super);
	}
	return null;
}

function initHierarchy (instance, args, factory) {
	const factoryWithInit = initMap.get(factory);
	
	if (!factoryWithInit) {	// neither factory nor any of its base factories have an init
		return;
	}
	
	// call 1st init in the chain
	// Unless that init is the base factory's, provide it with a function that calls the _next_ init in the chain
	// this is recursive, because the _next_ init might need a function that calls _its_ next init, etc
	const initArgs = args.slice();
	if (factoryWithInit.super) {
		initArgs.splice(0, 0, initHierarchy.bind(null, instance, args, factoryWithInit.super));
	}
	
	factoryWithInit.init.call(instance, ...initArgs);
}

export function createFactory (prototype, init = null) {
	const factory = function (...args) {
		const i = Object.create(prototype);
		
		initHierarchy(i, args, factory);
		
		return i;
	};
	
	factory.prototype = prototype;
	factory.init = init;
	
	initMap.set(factory, init ? factory : null);
	
	return factory;
}

export function extendFactory (base, prototype, init = null) {
	const proto = Object.create(base.prototype);
	
	mix(proto, prototype);
	
	const factory = createFactory(proto, init);
	
	factory.super = base;
	
	initMap.set(factory, findFactoryWithNextInit(factory));
	
	return factory;
}<commit_msg>create/extendFactory define factory properties to make them immutable<commit_after>/*jshint esnext:true*/

/**
 * @param	{Object} target	object to receive mixins
 * @param	{Object[]} mixins
 * @return	{Object}
 */
export function mix (target, ...mixins) {
	if (!target.hasOwnProperty('__mixins')) {
		target.__mixins = [];
	}
	
	for (let mixin of mixins) {
		// register mixin
		if (target.__mixins.indexOf(mixin) !== -1) {
			continue;	// already mixed in
		}
		target.__mixins.push(mixin);
		
		// copy properties
		for (let prop of Object.keys(mixin)) {
			if (!target.hasOwnProperty(prop) && prop !== 'constructor') {
				target[prop] = mixin[prop];
			}
		}
	}
	
	return target;
}

/**
 * 
 * @param {Object} prototype
 * @returns {Object}
 */
export function factoryFactory (prototype = Object.prototype) {
	let factory = Object.create(prototype);
	
	factory.create = function (...args) {
		let ob = Object.create(factory);
		
		prototype.init.apply(ob, args);
		
		return ob;
	};
	
	return factory;
}

const initMap = new Map();	// cache factory => (next) init

function findFactoryWithNextInit (factory) {
	if (factory.init) {
		return factory;
	}
	
	if (factory.super) {
		return findFactoryWithNextInit(factory.super);
	}
	return null;
}

function initHierarchy (instance, args, factory) {
	const factoryWithInit = initMap.get(factory);
	
	if (!factoryWithInit) {	// neither factory nor any of its base factories have an init
		return;
	}
	
	// call 1st init in the chain
	// Unless that init is the base factory's, provide it with a function that calls the _next_ init in the chain
	// this is recursive, because the _next_ init might need a function that calls _its_ next init, etc
	const initArgs = args.slice();
	if (factoryWithInit.super) {
		initArgs.splice(0, 0, initHierarchy.bind(null, instance, args, factoryWithInit.super));
	}
	
	factoryWithInit.init.call(instance, ...initArgs);
}

export function createFactory (prototype, init = null) {
	const factory = function (...args) {
		const i = Object.create(prototype);
		
		initHierarchy(i, args, factory);
		
		return i;
	};
	
	Object.defineProperties(factory, {
		prototype:		{value: prototype},
		init:			{value: init}
	});
	
	initMap.set(factory, init ? factory : null);
	
	return factory;
}

export function extendFactory (base, prototype, init = null) {
	const proto = Object.create(base.prototype);
	
	mix(proto, prototype);
	
	const factory = createFactory(proto, init);
	
	Object.defineProperties(factory, {
		super:			{value: base}
	});
	
	initMap.set(factory, findFactoryWithNextInit(factory));
	
	return factory;
}
{'fraction_non_alphanumeric': 0.13020214030915578, 'fraction_numerical': 0.0015854141894569957, 'mean_word_length': 3.7523540489642184, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '710061', 'n_tokens_mistral': 1742, 'n_tokens_neox': 1579, 'n_words': 464}