var CompilerBuffer = class CompilerBuffer {
	#content = "";
	newLine = "\n";
	writeStatement(statement) {
		this.#content = `${this.#content}${this.newLine}${statement}`;
	}
	child() {
		return new CompilerBuffer();
	}
	toString() {
		return this.#content;
	}
	flush() {
		this.#content = "";
	}
};
function defineFieldVariables({ parseFnRefId, variableName, wildCardPath, isArrayMember, valueExpression, parentExpression, fieldNameExpression, parentValueExpression }) {
	const inValueExpression = parseFnRefId ? `refs['${parseFnRefId}'](${valueExpression}, {
      data: root,
      meta: meta,
      parent: ${parentValueExpression}
    })` : valueExpression;
	let fieldPathOutputExpression = "";
	if (parentExpression === "root" || parentExpression === "root_item") fieldPathOutputExpression = fieldNameExpression;
	else if (fieldNameExpression !== "''") fieldPathOutputExpression = `${parentExpression}.getFieldPath() + '.' + ${fieldNameExpression}`;
	return `const ${variableName} = defineValue(${inValueExpression}, {
  data: root,
  meta: meta,
  name: ${fieldNameExpression},
  wildCardPath: '${wildCardPath}',
  getFieldPath() {
    return ${fieldPathOutputExpression};
  },
  mutate: defineValue,
  report: report,
  isValid: true,
  isValidDataType: false,
  parent: ${parentValueExpression},
  isArrayMember: ${isArrayMember},
});`;
}
function validateLiteralField({ variableName, validatorFnId }) {
	const rule = `refs['${validatorFnId}']`;
	return `${variableName}.isValidDataType = ${`${rule}.validator(${variableName}.value, ${rule}.options, ${variableName});`}`;
}
var BaseNode = class {
	#node;
	#parentField;
	field;
	constructor(node, compiler, parent, parentField) {
		this.#parentField = parentField;
		this.#node = node;
		if (this.#parentField) this.field = this.#parentField;
		else {
			compiler.variablesCounter++;
			this.field = compiler.createFieldFor(node, parent);
		}
	}
	defineField(buffer) {
		if (!this.#parentField) buffer.writeStatement(defineFieldVariables({
			fieldNameExpression: this.field.fieldNameExpression,
			isArrayMember: this.field.isArrayMember,
			parentExpression: this.field.parentExpression,
			parentValueExpression: this.field.parentValueExpression,
			valueExpression: this.field.valueExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath,
			parseFnRefId: "parseFnId" in this.#node ? this.#node.parseFnId : void 0
		}));
	}
};
function defineArrayGuard({ variableName, guardedCodeSnippet }) {
	return `if (${variableName}.isValidDataType) {
${guardedCodeSnippet}
}`;
}
function defineIsValidGuard({ variableName, bail, guardedCodeSnippet }) {
	if (!bail) return guardedCodeSnippet;
	return `if (${variableName}.isValid) {
${guardedCodeSnippet}
}`;
}
function defineFieldNullOutput({ allowNull, conditional, variableName, outputExpression, transformFnRefId }) {
	if (!allowNull) return "";
	return `${conditional || "if"}(${variableName}.value === null) {
  ${outputExpression} = ${transformFnRefId ? `refs['${transformFnRefId}'](null, ${variableName});` : "null;"}
}`;
}
function wrapInConditional(conditions, wrappingCode) {
	const [first, second] = conditions;
	if (first && second) return `if (${first} && ${second}) {
  ${wrappingCode}
}`;
	if (first) return `if (${first}) {
  ${wrappingCode}
}`;
	if (second) return `if (${second}) {
  ${wrappingCode}
}`;
	return wrappingCode;
}
function emitValidationSnippet({ isAsync, implicit, ruleFnId }, variableName, bail, dropMissingCheck, existenceCheckExpression) {
	const rule = `refs['${ruleFnId}']`;
	const callable = `${rule}.validator(${variableName}.value, ${rule}.options, ${variableName});`;
	existenceCheckExpression = existenceCheckExpression || `${variableName}.isDefined`;
	return wrapInConditional([bail ? `${variableName}.isValid` : "", implicit || dropMissingCheck ? "" : existenceCheckExpression], isAsync ? `await ${callable}` : `${callable}`);
}
function defineFieldValidations({ bail, validations, variableName, dropMissingCheck, existenceCheckExpression }) {
	return `${validations.map((one) => emitValidationSnippet(one, variableName, bail, dropMissingCheck, existenceCheckExpression)).join("\n")}`;
}
function defineArrayInitialOutput({ variableName, outputExpression, outputValueExpression }) {
	return `const ${variableName}_out = ${outputValueExpression};
${outputExpression} = ${variableName}_out;`;
}
function defineFieldExistenceValidations({ allowNull, isOptional, variableName }) {
	if (isOptional === false) if (allowNull === false) return `ensureExists(${variableName});`;
	else return `ensureIsDefined(${variableName});`;
	return "";
}
function validateArrayField({ variableName }) {
	return `${variableName}.isValidDataType = ensureIsArray(${variableName});`;
}
var TupleNodeCompiler = class extends BaseNode {
	#node;
	#buffer;
	#compiler;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
		this.#compiler = compiler;
	}
	#compileTupleChildren() {
		const buffer = this.#buffer.child();
		const parent = {
			type: "tuple",
			fieldPathExpression: this.field.fieldPathExpression,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath
		};
		this.#node.properties.forEach((child) => {
			this.#compiler.compileNode(child, buffer, parent);
		});
		return buffer.toString();
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(defineFieldExistenceValidations({
			allowNull: this.#node.allowNull,
			isOptional: this.#node.isOptional,
			variableName: this.field.variableName
		}));
		this.#buffer.writeStatement(validateArrayField({ variableName: this.field.variableName }));
		this.#buffer.writeStatement(defineFieldValidations({
			variableName: this.field.variableName,
			validations: this.#node.validations,
			bail: this.#node.bail,
			dropMissingCheck: false,
			existenceCheckExpression: `${this.field.variableName}.isValidDataType`
		}));
		const isArrayValidBlock = defineArrayGuard({
			variableName: this.field.variableName,
			guardedCodeSnippet: `${this.#buffer.newLine}${defineIsValidGuard({
				variableName: this.field.variableName,
				bail: this.#node.bail,
				guardedCodeSnippet: `${defineArrayInitialOutput({
					variableName: this.field.variableName,
					outputExpression: this.field.outputExpression,
					outputValueExpression: this.#node.allowUnknownProperties ? `copyProperties(${this.field.variableName}.value)` : `[]`
				})}${this.#buffer.newLine}${this.#compileTupleChildren()}`
			})}`
		});
		this.#buffer.writeStatement(`${isArrayValidBlock}${this.#buffer.newLine}${defineFieldNullOutput({
			allowNull: this.#node.allowNull,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			conditional: "else if"
		})}`);
	}
};
function defineArrayLoop({ variableName, loopCodeSnippet, startingIndex }) {
	startingIndex = startingIndex || 0;
	return `const ${variableName}_items_size = ${variableName}.value.length;
for (let ${variableName}_i = ${startingIndex}; ${variableName}_i < ${variableName}_items_size; ${variableName}_i++) {
${loopCodeSnippet}
}`;
}
var ArrayNodeCompiler = class extends BaseNode {
	#node;
	#buffer;
	#compiler;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
		this.#compiler = compiler;
	}
	#compileArrayElements() {
		const arrayElementsBuffer = this.#buffer.child();
		this.#compiler.compileNode(this.#node.each, arrayElementsBuffer, {
			type: "array",
			fieldPathExpression: this.field.fieldPathExpression,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath
		});
		const buffer = this.#buffer.child();
		buffer.writeStatement(defineArrayLoop({
			variableName: this.field.variableName,
			startingIndex: 0,
			loopCodeSnippet: arrayElementsBuffer.toString()
		}));
		arrayElementsBuffer.flush();
		return buffer.toString();
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(defineFieldExistenceValidations({
			allowNull: this.#node.allowNull,
			isOptional: this.#node.isOptional,
			variableName: this.field.variableName
		}));
		this.#buffer.writeStatement(validateArrayField({ variableName: this.field.variableName }));
		this.#buffer.writeStatement(defineFieldValidations({
			variableName: this.field.variableName,
			validations: this.#node.validations,
			bail: this.#node.bail,
			dropMissingCheck: false,
			existenceCheckExpression: `${this.field.variableName}.isValidDataType`
		}));
		const isArrayValidBlock = defineArrayGuard({
			variableName: this.field.variableName,
			guardedCodeSnippet: `${this.#buffer.newLine}${defineIsValidGuard({
				variableName: this.field.variableName,
				bail: this.#node.bail,
				guardedCodeSnippet: `${defineArrayInitialOutput({
					variableName: this.field.variableName,
					outputExpression: this.field.outputExpression,
					outputValueExpression: `[]`
				})}${this.#buffer.newLine}${this.#compileArrayElements()}`
			})}`
		});
		this.#buffer.writeStatement(`${isArrayValidBlock}${this.#buffer.newLine}${defineFieldNullOutput({
			allowNull: this.#node.allowNull,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			conditional: "else if"
		})}`);
	}
};
function callParseFunction({ parseFnRefId, variableName }) {
	if (parseFnRefId) return `${variableName}.value = refs['${parseFnRefId}'](${variableName}.value);`;
	return "";
}
function defineElseCondition({ variableName, conditionalFnRefId }) {
	return `else {
refs['${conditionalFnRefId}'](${variableName}.value, ${variableName});
}`;
}
function defineConditionalGuard({ conditional, variableName, conditionalFnRefId, guardedCodeSnippet }) {
	return `${conditional}(refs['${conditionalFnRefId}'](${variableName}.value, ${variableName})) {
${guardedCodeSnippet}
}`;
}
var UnionNodeCompiler = class extends BaseNode {
	#compiler;
	#node;
	#buffer;
	#parent;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
		this.#parent = parent;
		this.#compiler = compiler;
	}
	#compileUnionChildren() {
		const childrenBuffer = this.#buffer.child();
		this.#node.conditions.forEach((child, index) => {
			const conditionalBuffer = this.#buffer.child();
			if ("parseFnId" in child.schema) conditionalBuffer.writeStatement(callParseFunction({
				parseFnRefId: child.schema.parseFnId,
				variableName: this.field.variableName
			}));
			this.#compiler.compileNode(child.schema, conditionalBuffer, this.#parent, this.field);
			childrenBuffer.writeStatement(defineConditionalGuard({
				conditional: index === 0 ? "if" : "else if",
				variableName: this.field.variableName,
				conditionalFnRefId: child.conditionalFnRefId,
				guardedCodeSnippet: conditionalBuffer.toString()
			}));
			conditionalBuffer.flush();
		});
		if (this.#node.elseConditionalFnRefId && this.#node.conditions.length) childrenBuffer.writeStatement(defineElseCondition({
			variableName: this.field.variableName,
			conditionalFnRefId: this.#node.elseConditionalFnRefId
		}));
		return childrenBuffer.toString();
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(this.#compileUnionChildren());
	}
};
function defineRecordLoop({ variableName, loopCodeSnippet }) {
	return `const ${variableName}_keys = Object.keys(${variableName}.value);
const ${variableName}_keys_size = ${variableName}_keys.length;
for (let ${variableName}_key_i = 0; ${variableName}_key_i < ${variableName}_keys_size; ${variableName}_key_i++) {
const ${variableName}_i = ${variableName}_keys[${variableName}_key_i];
${loopCodeSnippet}
}`;
}
function defineObjectGuard({ variableName, guardedCodeSnippet }) {
	return `if (${variableName}.isValidDataType) {
${guardedCodeSnippet}
}`;
}
function defineObjectInitialOutput({ variableName, outputExpression, outputValueExpression }) {
	return `const ${variableName}_out = ${outputValueExpression};
${outputExpression} = ${variableName}_out;`;
}
function validateObjectField({ variableName }) {
	return `${variableName}.isValidDataType = ensureIsObject(${variableName});`;
}
var RecordNodeCompiler = class extends BaseNode {
	#node;
	#buffer;
	#compiler;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
		this.#compiler = compiler;
	}
	#compileRecordElements() {
		const buffer = this.#buffer.child();
		const recordElementsBuffer = this.#buffer.child();
		this.#compiler.compileNode(this.#node.each, recordElementsBuffer, {
			type: "record",
			fieldPathExpression: this.field.fieldPathExpression,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath
		});
		buffer.writeStatement(defineRecordLoop({
			variableName: this.field.variableName,
			loopCodeSnippet: recordElementsBuffer.toString()
		}));
		recordElementsBuffer.flush();
		return buffer.toString();
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(defineFieldExistenceValidations({
			allowNull: this.#node.allowNull,
			isOptional: this.#node.isOptional,
			variableName: this.field.variableName
		}));
		this.#buffer.writeStatement(validateObjectField({ variableName: this.field.variableName }));
		this.#buffer.writeStatement(defineFieldValidations({
			variableName: this.field.variableName,
			validations: this.#node.validations,
			bail: this.#node.bail,
			dropMissingCheck: false,
			existenceCheckExpression: `${this.field.variableName}.isValidDataType`
		}));
		const isObjectValidBlock = defineIsValidGuard({
			variableName: this.field.variableName,
			bail: this.#node.bail,
			guardedCodeSnippet: `${defineObjectInitialOutput({
				variableName: this.field.variableName,
				outputExpression: this.field.outputExpression,
				outputValueExpression: `{}`
			})}${this.#compileRecordElements()}`
		});
		const isValueAnObjectBlock = defineObjectGuard({
			variableName: this.field.variableName,
			guardedCodeSnippet: `${this.#buffer.newLine}${isObjectValidBlock}`
		});
		this.#buffer.writeStatement(`${isValueAnObjectBlock}${this.#buffer.newLine}${defineFieldNullOutput({
			allowNull: this.#node.allowNull,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			conditional: "else if"
		})}`);
	}
};
function arrayToString(arr) {
	return `[${arr.map((str) => `"${str}"`).join(", ")}]`;
}
function defineMoveProperties({ variableName, fieldsToIgnore, allowUnknownProperties }) {
	if (!allowUnknownProperties) return "";
	return `moveProperties(${variableName}.value, ${variableName}_out, ${arrayToString(fieldsToIgnore)});`;
}
var ObjectNodeCompiler = class extends BaseNode {
	#node;
	#buffer;
	#compiler;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
		this.#compiler = compiler;
	}
	#getFieldNames(node) {
		let fieldNames = node.properties.map((child) => child.fieldName);
		const groupsFieldNames = node.groups.flatMap((group) => this.#getGroupFieldNames(group));
		return fieldNames.concat(groupsFieldNames);
	}
	#getGroupFieldNames(group) {
		return group.conditions.flatMap((condition) => {
			return this.#getFieldNames(condition.schema);
		});
	}
	#compileObjectChildren() {
		const buffer = this.#buffer.child();
		const parent = {
			type: "object",
			fieldPathExpression: this.field.fieldPathExpression,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath
		};
		this.#node.properties.forEach((child) => this.#compiler.compileNode(child, buffer, parent));
		return buffer.toString();
	}
	#compileObjectGroups() {
		const buffer = this.#buffer.child();
		const parent = {
			type: "object",
			fieldPathExpression: this.field.fieldPathExpression,
			outputExpression: this.field.outputExpression,
			variableName: this.field.variableName,
			wildCardPath: this.field.wildCardPath
		};
		this.#node.groups.forEach((group) => this.#compileObjectGroup(group, buffer, parent));
		return buffer.toString();
	}
	#compileObjectGroup(group, buffer, parent) {
		group.conditions.forEach((condition, index) => {
			const guardBuffer = buffer.child();
			condition.schema.properties.forEach((child) => {
				this.#compiler.compileNode(child, guardBuffer, parent);
			});
			condition.schema.groups.forEach((child) => {
				this.#compileObjectGroup(child, guardBuffer, parent);
			});
			buffer.writeStatement(defineConditionalGuard({
				variableName: this.field.variableName,
				conditional: index === 0 ? "if" : "else if",
				conditionalFnRefId: condition.conditionalFnRefId,
				guardedCodeSnippet: guardBuffer.toString()
			}));
		});
		if (group.elseConditionalFnRefId && group.conditions.length) buffer.writeStatement(defineElseCondition({
			variableName: this.field.variableName,
			conditionalFnRefId: group.elseConditionalFnRefId
		}));
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(defineFieldExistenceValidations({
			allowNull: this.#node.allowNull,
			isOptional: this.#node.isOptional,
			variableName: this.field.variableName
		}));
		this.#buffer.writeStatement(validateObjectField({ variableName: this.field.variableName }));
		this.#buffer.writeStatement(defineFieldValidations({
			variableName: this.field.variableName,
			validations: this.#node.validations,
			bail: this.#node.bail,
			dropMissingCheck: false,
			existenceCheckExpression: `${this.field.variableName}.isValidDataType`
		}));
		const isObjectValidBlock = defineIsValidGuard({
			variableName: this.field.variableName,
			bail: this.#node.bail,
			guardedCodeSnippet: `${defineObjectInitialOutput({
				variableName: this.field.variableName,
				outputExpression: this.field.outputExpression,
				outputValueExpression: "{}"
			})}${this.#buffer.newLine}${this.#compileObjectChildren()}${this.#buffer.newLine}${this.#compileObjectGroups()}${this.#buffer.newLine}${defineMoveProperties({
				variableName: this.field.variableName,
				allowUnknownProperties: this.#node.allowUnknownProperties,
				fieldsToIgnore: this.#node.allowUnknownProperties ? this.#getFieldNames(this.#node) : []
			})}`
		});
		const isValueAnObject = defineObjectGuard({
			variableName: this.field.variableName,
			guardedCodeSnippet: `${isObjectValidBlock}`
		});
		this.#buffer.writeStatement(`${isValueAnObject}${this.#buffer.newLine}${defineFieldNullOutput({
			variableName: this.field.variableName,
			allowNull: this.#node.allowNull,
			outputExpression: this.field.outputExpression,
			conditional: "else if"
		})}`);
	}
};
function createRootField(parent) {
	return {
		parentExpression: parent.variableName,
		parentValueExpression: parent.variableName,
		fieldNameExpression: `''`,
		fieldPathExpression: `''`,
		wildCardPath: "",
		variableName: `${parent.variableName}_item`,
		valueExpression: "root",
		outputExpression: parent.outputExpression,
		isArrayMember: false
	};
}
function defineFieldValueOutput({ variableName, outputExpression, transformFnRefId }) {
	return `if (${variableName}.isDefined && ${variableName}.isValid) {
  ${outputExpression} = ${transformFnRefId ? `refs['${transformFnRefId}'](${variableName}.value, ${variableName})` : `${variableName}.value`};
}`;
}
var LiteralNodeCompiler = class extends BaseNode {
	#node;
	#buffer;
	constructor(node, buffer, compiler, parent, parentField) {
		super(node, compiler, parent, parentField);
		this.#node = node;
		this.#buffer = buffer;
	}
	compile() {
		this.defineField(this.#buffer);
		this.#buffer.writeStatement(defineFieldExistenceValidations({
			allowNull: this.#node.allowNull,
			isOptional: this.#node.isOptional,
			variableName: this.field.variableName
		}));
		if (this.#node.dataTypeValidatorFnId) this.#buffer.writeStatement(validateLiteralField({
			variableName: this.field.variableName,
			validatorFnId: this.#node.dataTypeValidatorFnId
		}));
		this.#buffer.writeStatement(defineFieldValidations({
			variableName: this.field.variableName,
			validations: this.#node.validations,
			bail: this.#node.bail,
			existenceCheckExpression: this.#node.dataTypeValidatorFnId ? `${this.field.variableName}.isValidDataType` : void 0,
			dropMissingCheck: false
		}));
		this.#buffer.writeStatement(`${defineFieldValueOutput({
			variableName: this.field.variableName,
			outputExpression: this.field.outputExpression,
			transformFnRefId: this.#node.transformFnId
		})}${this.#buffer.newLine}${defineFieldNullOutput({
			variableName: this.field.variableName,
			allowNull: this.#node.allowNull,
			outputExpression: this.field.outputExpression,
			transformFnRefId: this.#node.transformFnId,
			conditional: "else if"
		})}`);
	}
};
function createArrayField(parent) {
	const wildCardPath = parent.wildCardPath !== "" ? `${parent.wildCardPath}.*` : `*`;
	return {
		parentExpression: parent.variableName,
		parentValueExpression: `${parent.variableName}.value`,
		fieldNameExpression: `${parent.variableName}_i`,
		fieldPathExpression: wildCardPath,
		wildCardPath,
		variableName: `${parent.variableName}_item`,
		valueExpression: `${parent.variableName}.value[${parent.variableName}_i]`,
		outputExpression: `${parent.variableName}_out[${parent.variableName}_i]`,
		isArrayMember: true
	};
}
function createTupleField(node, parent) {
	const wildCardPath = parent.wildCardPath !== "" ? `${parent.wildCardPath}.${node.fieldName}` : node.fieldName;
	return {
		parentExpression: parent.variableName,
		parentValueExpression: `${parent.variableName}.value`,
		fieldNameExpression: `${node.fieldName}`,
		fieldPathExpression: wildCardPath,
		wildCardPath,
		variableName: `${parent.variableName}_item_${node.fieldName}`,
		valueExpression: `${parent.variableName}.value[${node.fieldName}]`,
		outputExpression: `${parent.variableName}_out[${node.propertyName}]`,
		isArrayMember: true
	};
}
function reportErrors() {
	return `if(errorReporter.hasErrors) {
  throw errorReporter.createError();
}`;
}
const NUMBER_CHAR_RE = /\d/;
const VALID_CHARS = /[A-Za-z0-9]+/;
function isUppercase(char = "") {
	if (NUMBER_CHAR_RE.test(char)) return;
	return char !== char.toLowerCase();
}
function upperFirst(value) {
	return value ? value[0].toUpperCase() + value.slice(1) : "";
}
function lowerFirst(value) {
	return value ? value[0].toLowerCase() + value.slice(1) : "";
}
function splitByCase(value) {
	const parts = [];
	if (!value || typeof value !== "string") return parts;
	let buff = "";
	let previousUpper;
	let previousSplitter;
	for (const char of value) {
		const isSplitter = !VALID_CHARS.test(char);
		if (isSplitter === true) {
			parts.push(buff);
			buff = "";
			previousUpper = void 0;
			continue;
		}
		const isUpper = isUppercase(char);
		if (previousSplitter === false) {
			if (previousUpper === false && isUpper === true) {
				parts.push(buff);
				buff = char;
				previousUpper = isUpper;
				continue;
			}
			if (previousUpper === true && isUpper === false && buff.length > 1) {
				const lastChar = buff.at(-1);
				parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
				buff = lastChar + char;
				previousUpper = isUpper;
				continue;
			}
		}
		buff += char;
		previousUpper = isUpper;
		previousSplitter = isSplitter;
	}
	parts.push(buff);
	return parts;
}
function toVariableName(value) {
	const pascalCase = splitByCase(value).map((p) => upperFirst(p.toLowerCase())).join("");
	return /^[0-9]+/.test(pascalCase) ? `var_${pascalCase}` : lowerFirst(pascalCase);
}
function createObjectField(node, variablesCounter, parent) {
	const wildCardPath = parent.wildCardPath !== "" ? `${parent.wildCardPath}.${node.fieldName}` : node.fieldName;
	return {
		parentExpression: parent.variableName,
		parentValueExpression: `${parent.variableName}.value`,
		fieldNameExpression: `'${node.fieldName}'`,
		fieldPathExpression: wildCardPath,
		wildCardPath,
		variableName: `${toVariableName(node.propertyName)}_${variablesCounter}`,
		valueExpression: `${parent.variableName}.value['${node.fieldName}']`,
		outputExpression: `${parent.variableName}_out['${node.propertyName}']`,
		isArrayMember: false
	};
}
function createRecordField(parent) {
	const wildCardPath = parent.wildCardPath !== "" ? `${parent.wildCardPath}.*` : `*`;
	return {
		parentExpression: parent.variableName,
		parentValueExpression: `${parent.variableName}.value`,
		fieldNameExpression: `${parent.variableName}_i`,
		fieldPathExpression: wildCardPath,
		wildCardPath,
		variableName: `${parent.variableName}_item`,
		valueExpression: `${parent.variableName}.value[${parent.variableName}_i]`,
		outputExpression: `${parent.variableName}_out[${parent.variableName}_i]`,
		isArrayMember: false
	};
}
function defineInlineFunctions(options) {
	return `function report(message, rule, field, args) {
  field.isValid = false;
  errorReporter.report(messagesProvider.getMessage(message, rule, field, args), rule, field, args);
};
function defineValue(value, field) {
  ${options.convertEmptyStringsToNull ? `if (typeof value === 'string' && value.trim() === '') { value = null; }` : ""}
  field.value = value;
  field.isDefined = value !== undefined && value !== null;
  return field;
};
function ensureExists(field) {
  if (field.value === undefined || field.value === null) {
    field.report(REQUIRED, 'required', field);
    return false;
  }
  return true;
};
function ensureIsDefined(field) {
  if (field.value === undefined) {
    field.report(REQUIRED, 'required', field);
    return false;
  }
  return true;
};
function ensureIsObject(field) {
  if (!field.isDefined) {
    return false;
  }
  if (typeof field.value == 'object' && !Array.isArray(field.value)) {
    return true;
  }
  field.report(NOT_AN_OBJECT, 'object', field);
  return false;
};
function ensureIsArray(field) {
  if (!field.isDefined) {
    return false;
  }
  if (Array.isArray(field.value)) {
    return true;
  }
  field.report(NOT_AN_ARRAY, 'array', field);
  return false;
};
function copyProperties(val) {
  let k, out, tmp;

  if (Array.isArray(val)) {
    out = Array((k = val.length))
    while (k--) out[k] = (tmp = val[k]) && typeof tmp == 'object' ? copyProperties(tmp) : tmp
    return out
  }

  if (Object.prototype.toString.call(val) === '[object Object]') {
    out = {} // null
    for (k in val) {
      out[k] = (tmp = val[k]) && typeof tmp == 'object' ? copyProperties(tmp) : tmp
    }
    return out
  }
  return val
};
function moveProperties(source, destination, ignoreKeys) {
  for (let key in source) {
    if (!ignoreKeys.includes(key)) {
      const value = source[key]
      destination[key] = copyProperties(value)
    }
  }
};`;
}
function defineInlineErrorMessages(messages) {
	return `const REQUIRED = '${messages.required}';
const NOT_AN_OBJECT = '${messages.object}';
const NOT_AN_ARRAY = '${messages.array}';`;
}
const AsyncFunction = Object.getPrototypeOf(async function() {}).constructor;
var Compiler = class {
	variablesCounter = 0;
	#rootNode;
	#options;
	#buffer = new CompilerBuffer();
	constructor(rootNode, options) {
		this.#rootNode = rootNode;
		this.#options = options || { convertEmptyStringsToNull: false };
	}
	#initiateJSOutput() {
		this.#buffer.writeStatement(defineInlineErrorMessages({
			required: "value is required",
			object: "value is not a valid object",
			array: "value is not a valid array",
			...this.#options.messages
		}));
		this.#buffer.writeStatement(defineInlineFunctions(this.#options));
		this.#buffer.writeStatement("let out;");
	}
	#finishJSOutput() {
		this.#buffer.writeStatement(reportErrors());
		this.#buffer.writeStatement("return out;");
	}
	#compileNodes() {
		this.compileNode(this.#rootNode.schema, this.#buffer, {
			type: "root",
			variableName: "root",
			outputExpression: "out",
			fieldPathExpression: "out",
			wildCardPath: ""
		});
	}
	#toAsyncFunction() {
		return new AsyncFunction("root", "meta", "refs", "messagesProvider", "errorReporter", this.#buffer.toString());
	}
	createFieldFor(node, parent) {
		switch (parent.type) {
			case "array": return createArrayField(parent);
			case "root": return createRootField(parent);
			case "object": return createObjectField(node, this.variablesCounter, parent);
			case "tuple": return createTupleField(node, parent);
			case "record": return createRecordField(parent);
		}
	}
	compileNode(node, buffer, parent, parentField) {
		switch (node.type) {
			case "literal": return new LiteralNodeCompiler(node, buffer, this, parent, parentField).compile();
			case "array": return new ArrayNodeCompiler(node, buffer, this, parent, parentField).compile();
			case "record": return new RecordNodeCompiler(node, buffer, this, parent, parentField).compile();
			case "object": return new ObjectNodeCompiler(node, buffer, this, parent, parentField).compile();
			case "tuple": return new TupleNodeCompiler(node, buffer, this, parent, parentField).compile();
			case "union": return new UnionNodeCompiler(node, buffer, this, parent, parentField).compile();
		}
	}
	compile() {
		this.#initiateJSOutput();
		this.#compileNodes();
		this.#finishJSOutput();
		const outputFunction = this.#toAsyncFunction();
		this.variablesCounter = 0;
		this.#buffer.flush();
		return outputFunction;
	}
};
function refsBuilder() {
	let counter = 0;
	const refs = {};
	return {
		toJSON() {
			return refs;
		},
		track(value) {
			counter++;
			const ref = `ref://${counter}`;
			refs[ref] = value;
			return ref;
		},
		trackValidation(validation) {
			return this.track(validation);
		},
		trackParser(fn) {
			return this.track(fn);
		},
		trackTransformer(fn) {
			return this.track(fn);
		},
		trackConditional(fn) {
			return this.track(fn);
		}
	};
}
export { Compiler, refsBuilder };
