signet
Version:
Signet type library
1,661 lines (1,260 loc) • 74.2 kB
JavaScript
var signetAssembler = (function () {
'use strict';
function hasSubtype(typeDef) {
return typeDef.subtype && typeDef.subtype.length > 0;
}
function buildSubtype(typeDef) {
return hasSubtype(typeDef) ? '<' + typeDef.subtype.join(';') + '>' : '';
}
function prependTypeName(name, typeStr) {
return typeof name === 'string' ? name + ':' + typeStr : typeStr;
}
function addOptionalBrackets(typeStr, isOptional) {
return isOptional ? '[' + typeStr + ']' : typeStr;
}
function getBaseType(typeDef) {
var typeStr = typeDef.type + buildSubtype(typeDef);
return addOptionalBrackets(typeStr, typeDef.optional);
}
function assembleType(typeDef) {
var typeStr = getBaseType(typeDef);
return prependTypeName(typeDef.name, typeStr);
}
function buildDependentToken (result, dependent) {
var output = result !== '' ? result + ', ' : result;
return output + [dependent.left, dependent.operator, dependent.right].join(' ');
}
function buildDependentStr (dependent) {
return dependent.reduce(buildDependentToken, '') + ' :: ';
}
function assembleTypeList(typeList) {
var typeListStr = typeList.map(assembleType).join(', ');
var dependentStr = typeList.dependent === null ? '' : buildDependentStr(typeList.dependent);
return dependentStr + typeListStr;
}
function assembleSignature(typeTree) {
return typeTree.map(assembleTypeList).join(' => ');
}
return {
assembleSignature: assembleSignature,
assembleType: assembleType
};
})();
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetAssembler;
}
var signetChecker = (function () {
'use strict';
return function (registrar) {
function checkType(typeDef) {
try {
return typeof registrar.get(typeDef.type) === 'function';
} catch (e) {
return false;
}
}
function concat(resultList, list) {
return resultList.concat(list);
}
function not(predicate) {
return function (value) {
return !predicate(value);
}
}
function checkSignature(ast) {
var failedTypes = ast.reduce(concat, [])
.filter(not(checkType));
return failedTypes.length > 0 ? failedTypes : null;
}
return {
checkSignature: checkSignature,
checkType: checkType
};
}
})();
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetChecker;
}
function signetParser() {
'use strict';
var typeLevelMacros = [];
var signatureLevelMacros = [];
function throwOnBadMacroResult(result) {
if (typeof result !== 'string') {
throw new Error('Macro Error: All macros must return a string; got ' + result + ' of type ' + typeof result);
}
}
function applyMacros(macroSet, typeStr) {
var result = typeStr;
var macroLength = macroSet.length;
for (var i = 0; i < macroLength; i++) {
result = macroSet[i](result);
throwOnBadMacroResult(result);
}
return result;
}
function registerTypeLevelMacro(macro) {
typeLevelMacros.push(macro);
}
function registerSignatureLevelMacro(macro) {
signatureLevelMacros.push(macro);
}
function getSubtypeData(typeStr) {
var subtypeToken = typeStr.trim().split('<').slice(1).join('<');
return subtypeToken.substring(0, subtypeToken.length - 1);
}
function isSubtypeSeparator(value) {
return value === ';' || value === ',';
}
function parseSubtype(typeStr) {
var optionalPattern = /^\[(.*)\]$/
var subtypeData = getSubtypeData(typeStr.trim().replace(optionalPattern, '$1'));
return splitOnSymbol(isSubtypeSeparator, subtypeData)
.map(function (value) { return value.trim(); });
}
function getColonPosition(typeStr) {
var colonPosition = -1;
var lastChar = '';
for (var i = 0; i < typeStr.length && lastChar !== '<' && colonPosition === -1; i++) {
lastChar = typeStr[i];
if (lastChar === ':') {
colonPosition = i;
}
}
return colonPosition;
}
function typeParser(typeStr) {
var transformedTypeStr = applyMacros(typeLevelMacros, typeStr);
return {
type: transformedTypeStr.split('<')[0].replace(/\[|\]/g, '').trim(),
subtype: parseSubtype(transformedTypeStr),
optional: transformedTypeStr.trim().match(/^\[[^\]]+\]$/) !== null
};
}
function isObjectInstance(value) {
return typeof value === 'object' && value !== null;
}
function isArray(value) {
return isObjectInstance(value)
&& Object.prototype.toString.call(value) === '[object Array]';
}
function copyArray(values) {
var result = [];
for (var i = 0; i < values.length; i++) {
result.push(copyObjectOrReturn(values[i]));
}
result.dependent = isArray(values.dependent)
? copyArray(values.dependent)
: values.dependent;
return result;
}
function copyObjectOrReturn(value) {
if (isArray(value)) {
return copyArray(value);
} else if (isObjectInstance(value)) {
return copyProps(value);
} else {
return value;
}
}
function copyProps(obj) {
var keys = Object.keys(obj);
var result = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = obj[key];
result[key] = copyObjectOrReturn(value);
}
return result;
}
function copyMemoizerFactory(parser) {
var memoizedTypes = {};
return function (typeStr) {
var parsedType = memoizedTypes[typeStr];
if (typeof parsedType === 'undefined') {
parsedType = parser(typeStr);
memoizedTypes[typeStr] = parsedType;
}
return isArray(parsedType) ? copyArray(parsedType) : copyProps(parsedType);
}
}
var memoizedTypeParser = copyMemoizerFactory(typeParser);
function parseType(typeStr) {
var colonPosition = typeStr.indexOf(':') > -1 ? getColonPosition(typeStr) : -1;
var typeName = colonPosition === -1 ? null : typeStr.substring(0, colonPosition).trim();
var rawType = typeStr.substring(colonPosition + 1);
var parsedType = memoizedTypeParser(rawType);
parsedType.name = typeName;
return parsedType;
}
function parseDependentMetadataToken(metadataStr) {
var tokens = metadataStr.trim().split(/\s+/g);
return {
operator: tokens[1],
left: tokens[0],
right: tokens[2]
}
}
function parseDependentMetadata(metadataStr) {
return metadataStr.split(/\,\s*/g).map(parseDependentMetadataToken);
}
function isComma(symbol) {
return symbol === ',';
}
function isDoubleColon(symbol) {
return symbol === '::';
}
function parseParams(token) {
var tokenSet = splitOnSymbol(isDoubleColon, token);
var dependentMetadata = tokenSet.length > 1 ? tokenSet.shift() : null;
var typeValues = splitOnSymbol(isComma, tokenSet[0]).map(parseType);
typeValues.dependent = dependentMetadata === null ? null : parseDependentMetadata(dependentMetadata);
return typeValues;
}
function bracketStackFactory() {
var stack = [];
function update(symbol) {
if (symbol === '<') {
stack.push('<');
}
if (symbol === '>') {
stack.pop();
}
if (symbol === '::') {
stack.length = 0;
}
}
return {
update: update,
get length() {
return stack.length;
}
};
}
function isSequenceChar(symbol) {
return symbol === '=' ||
symbol === '%' ||
symbol === ':';
}
function isSpecialSquence(symbol) {
return symbol[0] === '%' ||
symbol === '=>' ||
symbol === '::';
}
function splitOnSymbol(isSplitSymbol, signature) {
var tokens = [];
var currentToken = '';
var currentSymbol = '';
var bracketStack = bracketStackFactory();
for (var i = 0; i < signature.length; i++) {
currentSymbol = signature[i];
if (bracketStack.length === 0 && currentSymbol === '%') {
i++;
currentToken += signature[i];
continue;
}
if (isSequenceChar(currentSymbol) && isSpecialSquence(currentSymbol + signature[i + 1])) {
i++;
currentSymbol = currentSymbol + signature[i];
}
bracketStack.update(currentSymbol);
if (isSplitSymbol(currentSymbol) && bracketStack.length === 0) {
tokens.push(currentToken);
currentToken = '';
continue;
}
currentToken += currentSymbol;
}
if (currentToken !== '') {
tokens.push(currentToken);
}
return tokens;
}
function isArrow(symbol) {
return symbol === '=>';
}
function signatureParser(signature) {
var resolvedSignature = applyMacros(signatureLevelMacros, signature);
return splitOnSymbol(isArrow, resolvedSignature).map(parseParams);
}
var parseSignature = copyMemoizerFactory(signatureParser);
return {
parseSignature: parseSignature,
parseType: parseType,
registerSignatureLevelMacro: registerSignatureLevelMacro,
registerTypeLevelMacro: registerTypeLevelMacro
};
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetParser;
}
var signetRegistrar = (function () {
'use strict';
return function () {
function isTypeOf(type, value) {
return typeof value === type;
}
function isValidTypeName(value) {
return isTypeOf('string', value) && value.match(/^[^\(\)\<\>\[\]\:\;\=\,\s]+$/) !== null;
}
function throwOnBadType(name, predicate) {
if (!isValidTypeName(name)) {
throw new Error('Invalid type name: ' + name);
}
if (!isTypeOf('undefined', registry[name])) {
throw new Error('Type already registered with name ' + name);
}
if (!isTypeOf('function', predicate)) {
throw new Error('Type predicate parameter must be a function');
}
}
// Core registry code
var registry = {};
function get(name) {
var predicate = registry[name];
if (typeof predicate === 'undefined') {
throw new Error('The given type "' + name + '" does not exist');
}
return predicate;
}
function set(name, predicate) {
throwOnBadType(name, predicate);
registry[name] = predicate;
}
return {
get: get,
set: set
};
};
})();
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetRegistrar;
}
var signetTypelog = function (registrar) {
'use strict';
registrar.set('*', function () { return true; });
function validateOptionalType(typeDef) {
return function (value) {
return typeDef.optional && typeof value === 'undefined';
};
}
function validateType(typeDef) {
var validateOptional = validateOptionalType(typeDef);
var typePredicate = typeDef.predicate;
return function (value) {
return typePredicate(value, typeDef.subtype)
|| validateOptional(value);
};
}
function setImmutableProperty(obj, name, value) {
Object.defineProperty(obj, name, {
value: value,
writeable: false
});
}
function buildSubtypeCheck(parentTypeName, parentSubtypeCheck) {
return function (typeName) {
return parentTypeName === typeName || parentSubtypeCheck(typeName);
}
}
function buildTypePredicate(parentPredicate, childPredicate) {
return function (value, options) {
return parentPredicate(value, []) && childPredicate(value, options);
}
}
function merge(destination, source) {
return Object
.keys(source)
.reduce(function (result, key){
result[key] = source[key];
return result
}, destination);
}
function alwaysFalse () { return false; }
function getSubtypeCheck(predicate) {
return typeof predicate.isSubtypeOf === 'function'
? predicate.isSubtypeOf
: alwaysFalse;
}
function defineSubtypeOf(parentName) {
var parentPredicate = registrar.get(parentName);
var parentSubtypeCheck = getSubtypeCheck(parentPredicate);
return function (childName, childPredicate) {
var typePredicate = buildTypePredicate(parentPredicate, childPredicate);
var isSubtypeOfParent = buildSubtypeCheck(parentName, parentSubtypeCheck);
merge(typePredicate, childPredicate);
setImmutableProperty(typePredicate, 'parentTypeName', parentName);
setImmutableProperty(typePredicate, 'isSubtypeOf', isSubtypeOfParent);
registrar.set(childName, typePredicate);
};
}
function isType(typeName) {
try {
return typeof registrar.get(typeName) === 'function';
} catch (e) {
return false;
}
}
function isSubtypeOfBuilder(parentName) {
return memoize(function (childName) {
var subtypeCheck = registrar.get(childName).isSubtypeOf;
return subtypeCheck(parentName);
});
}
var isSubtypeOf = memoize(isSubtypeOfBuilder);
function memoize(action) {
var memoStore = {};
return function (value) {
var key = JSON.stringify(value);
if(typeof memoStore[key] === 'undefined') {
memoStore[key] = action(value);
}
return memoStore[key];
}
}
function isTypeOfFactory(typeDef) {
var processedTypeDef = preprocessSubtypeData(typeDef);
return function (value) {
return validateType(processedTypeDef)(value);
};
}
var isTypeOf = memoize(isTypeOfFactory);
function identity(value) {
return value;
}
function preprocessSubtypeData(typeDef) {
var predicate = registrar.get(typeDef.type);
var preprocess = typeof predicate.preprocess === 'function' ? predicate.preprocess : identity;
var typeDefClone = merge({}, typeDef);
typeDefClone.subtype = preprocess(typeDef.subtype);
typeDefClone.predicate = predicate;
return typeDefClone;
}
function getTypeChain(typeName) {
var predicate = registrar.get(typeName);
return predicate.parentTypeName !== undefined
? getTypeChain(predicate.parentTypeName) + ' -> ' + typeName
: typeName;
}
function defineDependentOperatorOn(typeName) {
var typePred = registrar.get(typeName);
return function (operator, operation) {
var operatorDef = {
operator: operator,
operation: operation
};
setImmutableProperty(typePred, operator, operatorDef);
}
}
function getDependentOperatorOn(typeName) {
return function (operator) {
var typePred = registrar.get(typeName);
if (typeof typePred[operator] === 'object') {
return typePred[operator];
} else if (typeName == '*') {
return null;
} else {
return getDependentOperatorOn(typePred.parentTypeName)(operator);
}
}
}
return {
define: defineSubtypeOf('*'),
defineDependentOperatorOn: defineDependentOperatorOn,
defineSubtypeOf: defineSubtypeOf,
getDependentOperatorOn: getDependentOperatorOn,
getTypeChain: getTypeChain,
isType: isType,
isTypeOf: isTypeOf,
isSubtypeOf: isSubtypeOf
};
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetTypelog;
}
var signetValidator = (function () {
'use strict';
function first(list) {
return list[0];
}
function rest(list) {
return list.slice(1);
}
return function (typelog, assembler, parser) {
function validateOptional(typeDef, argument, typeList) {
return typeDef.optional && (typeList.length > 1 || typeof argument === 'undefined');
}
function validateType(typeDef) {
var hasTypeCheck = typeof typeDef.typeCheck === 'function';
return hasTypeCheck ? typeDef.typeCheck : typelog.isTypeOf(typeDef);
}
function validateCurrentValue(typeList, argumentList) {
var typeDef = first(typeList);
var argument = first(argumentList);
var isValidated = validateType(typeDef)(argument);
var nextArgs = !isValidated ? argumentList : rest(argumentList);
var validateNext = validateArguments(rest(typeList));
var accepted = isValidated || validateOptional(typeDef, argument, typeList);
return accepted ? validateNext(nextArgs) : [assembler.assembleType(typeDef), argument];
}
function getValidationState(left, right, operatorDef, dependent) {
var validationState = null;
if (!operatorDef.operation(left.value, right.value, left.typeNode, right.typeNode, dependent)) {
var typeInfo = [left.name, operatorDef.operator, right.name];
var typeDef = typeInfo.join(' ');
var valueInfo = [left.name, '=', left.value, 'and', right.name, '=', right.value];
validationState = [typeDef, valueInfo.join(' ')];
}
return validationState;
}
function alwaysFalse() {
return false;
}
function getDependentOperator(typeName, operator) {
var dependentOperator = typelog.getDependentOperatorOn(typeName)(operator);
if (dependentOperator === null) {
dependentOperator = {
operator: operator,
operation: alwaysFalse
};
}
return dependentOperator;
}
function buildTypeObj(typeName) {
var typeDef = parser.parseType(typeName);
var isCorrectType = typelog.isTypeOf(typeDef);
function typeCheck(value) {
return isCorrectType(value);
}
typeCheck.toString = function () {
return '[function typePredicate]';
}
return {
name: typeName,
value: typeCheck,
typeNode: typeDef
};
}
function getRightArg(namedArgs, right) {
var value = namedArgs[right];
if (typeof value === 'undefined' && typelog.isType(right)) {
value = buildTypeObj(right);
}
return value;
}
function checkDependentTypes(namedArgs) {
return function (dependent, validationState) {
dependent.leftTokens = dependent.left.split(':');
dependent.rightTokens = dependent.right.split(':');
var leftName = dependent.leftTokens[0];
var left = namedArgs[leftName];
var rightName = dependent.rightTokens[0];
var right = getRightArg(namedArgs, rightName);
var newValidationState = null;
var namedValuesExist =
typeof left !== 'undefined'
&& typeof right !== 'undefined';
if (validationState === null && namedValuesExist) {
var operatorDef = getDependentOperator(left.typeNode.type, dependent.operator);
newValidationState = getValidationState(left, right, operatorDef, dependent);
}
return newValidationState === null ? validationState : newValidationState;
};
}
function buildEnvironmentTable(typeList, argumentList, environment) {
var result = typeof environment === 'undefined' ? {} : environment;
var typeLength = typeList.length;
var typeNode;
var typeName;
for (var i = 0; i < typeLength; i++) {
typeNode = typeList[i];
typeName = typeNode.name;
if (typeName === null) {
continue;
}
if (typeof result[typeName] !== 'undefined') {
var errorMessage = 'Signet evaluation error: '
+ 'Cannot overwrite value in existing variable: "'
+ typeName + '"';
throw new Error(errorMessage);
}
result[typeName] = {
name: typeName,
value: argumentList[i],
typeNode: typeList[i]
};
}
return result;
}
function arrayOrDefault(value) {
var typeOk = Object.prototype.toString.call(value) === '[object Array]';
return typeOk ? value : [];
}
function validateArguments(typeList, environment) {
var dependentExpressions = arrayOrDefault(typeList.dependent);
return function (argumentList) {
var startingEnvironment = typeof environment === 'undefined'
? typeList.environment
: environment;
var environmentTable = buildEnvironmentTable(typeList, argumentList, startingEnvironment);
var checkDependentType = checkDependentTypes(environmentTable);
var validationState = typeList.length === 0 ? null : validateCurrentValue(typeList, argumentList);
dependentExpressions.forEach(function (dependent) {
validationState = checkDependentType(dependent, validationState);
});
return validationState;
};
}
return {
validateArguments: validateArguments,
validateType: validateType,
buildEnvironment: buildEnvironmentTable
};
};
})();
if (typeof module !== 'undefined' && typeof module.exports !== undefined) {
module.exports = signetValidator;
}
function signetDuckTypes(typelog, isTypeOf, parseType, assembleType) {
var duckTypeErrorReporters = {};
function defineDuckType(typeName, objectDef) {
var duckType = buildDuckType(objectDef);
var duckTypeErrorReporter = buildDuckTypeErrorReporter(objectDef);
typelog.defineSubtypeOf('object')(typeName, duckType);
duckTypeErrorReporters[typeName] = duckTypeErrorReporter;
}
function getValueType(propertyValue) {
var propertyType = typeof propertyValue;
return isTypeOf('array')(propertyValue)
? 'array'
: propertyType;
}
function buildDuckTypeObject(propertyList, baseObject) {
return propertyList.reduce(function (result, key) {
if (key !== 'constructor') {
var propertyValue = baseObject[key];
var propertyType = getValueType(propertyValue);
result[key] = propertyType;
}
return result
}, {});
}
function isPrototypalObject(value) {
return typeof value.prototype === 'object';
}
function throwIfNotPrototypalObject(value) {
if (!isPrototypalObject(value)) {
var message = "Function defineClassType expected a prototypal object or class, but got a value of type " + typeof value;
throw new TypeError(message);
}
}
function mergeTypeProps(destinationObject, propsObject) {
Object.keys(propsObject).forEach(function (key) {
if (isTypeOf('not<undefined>')(destinationObject[key])) {
var message = 'Cannot reassign property ' + key + ' on duck type object';
throw new Error(message);
}
destinationObject[key] = propsObject[key];
});
}
function getDuckTypeObject(prototypalObject, otherProps) {
throwIfNotPrototypalObject(prototypalObject);
var prototype = prototypalObject.prototype;
var propertyList = Object.getOwnPropertyNames(prototype);
var duckTypeObject = buildDuckTypeObject(propertyList, prototype);
if (isTypeOf('composite<not<null>, object>')(otherProps)) {
mergeTypeProps(duckTypeObject, otherProps);
}
return duckTypeObject
}
function defineClassType(prototypalObject, otherProps) {
var className = prototypalObject.name;
var duckTypeObject = getDuckTypeObject(prototypalObject, otherProps)
defineDuckType(className, duckTypeObject);
}
function classTypeFactory(prototypalObject, otherProps) {
var duckTypeObject = getDuckTypeObject(prototypalObject, otherProps);
return duckTypeFactory(duckTypeObject);
}
function getErrorValue(value, typeName) {
if (typeof duckTypeErrorReporters[typeName] === 'function') {
return duckTypeErrorReporters[typeName](value);
}
return value;
}
var isString = isTypeOf('string');
function getTypeName(objectDef, key) {
return typeof objectDef[key] === 'string' ? objectDef[key] : objectDef[key].name;
}
function buildTypeResolvedDefinition(keys, objectDef) {
var result = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var typeValue = objectDef[key];
result[key] = isString(typeValue)
? assembleType(parseType(typeValue))
: typeValue
}
return result;
}
function isObjectInstance(value) {
return typeof value === 'object' && value !== null;
}
function passThrough(result) {
return result;
}
function buildTestAndReport(key, typeName, typePredicate) {
return function (result, value) {
if (!typePredicate(value[key])) {
result.push([key, typeName, getErrorValue(value[key], typeName)]);
}
return result;
};
}
function combineReporters(reporter1, reporter2) {
return function (result, value) {
result = reporter1(result, value);
return reporter2(result, value);
};
}
function buildDuckTypeReporter(keys, objectDef, typeResolvedDefinition) {
var testAndReport = passThrough;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var typePredicate = isTypeOf(objectDef[key]);
var typeName = getTypeName(typeResolvedDefinition, key);
var currentReporter = buildTestAndReport(key, typeName, typePredicate);
testAndReport = combineReporters(testAndReport, currentReporter);
}
return function (value) {
return testAndReport([], value);
};
}
function buildDuckTypeErrorReporter(objectDef) {
var keys = Object.keys(objectDef);
var typeResolvedDefinition = buildTypeResolvedDefinition(keys, objectDef);
var duckTypeReporter = buildDuckTypeReporter(keys, objectDef, typeResolvedDefinition);
return function (value) {
if (!isObjectInstance(value)) {
return [['badDuckTypeValue', 'object', value]]
}
return duckTypeReporter(value);
};
}
var isDuckTypeCheckable = isTypeOf('composite<not<null>, variant<object, function>>')
function alwaysTrue() { return true; }
function buildPropCheck(propCheck, key, type) {
var typeCheck = isTypeOf(type);
if(typeof typeCheck !== 'function') {
typeCheck = buildDuckType(type);
}
return function (obj) {
return typeCheck(obj[key]) && propCheck(obj);
}
}
function buildDuckType(definition) {
var keys = Object.keys(definition);
var typeCheck = alwaysTrue;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
typeCheck = buildPropCheck(typeCheck, key, definition[key])
}
return function (obj) {
return isDuckTypeCheckable(obj) && typeCheck(obj);
}
}
function duckTypeFactory(objectDef) {
return buildDuckType(objectDef);
}
function reportDuckTypeErrors(typeName) {
var errorChecker = duckTypeErrorReporters[typeName];
if (typeof errorChecker === 'undefined') {
throw new Error('No duck type "' + typeName + '" exists.');
}
return function (value) {
return errorChecker(value);
}
}
function exactDuckTypeFactory(objectDef) {
var propertyLength = Object.keys(objectDef).length;
var duckType = duckTypeFactory(objectDef);
return function (value) {
return propertyLength === Object.keys(value).length && duckType(value);
};
}
function defineExactDuckType(typeName, objectDef) {
var duckType = exactDuckTypeFactory(objectDef);
var duckTypeErrorReporter = buildDuckTypeErrorReporter(objectDef);
typelog.defineSubtypeOf('object')(typeName, duckType);
duckTypeErrorReporters[typeName] = duckTypeErrorReporter;
}
function isRegisteredDuckType(typeName) {
return typeof duckTypeErrorReporters[typeName] === 'function';
}
return {
buildDuckTypeErrorChecker: buildDuckTypeErrorReporter,
classTypeFactory: classTypeFactory,
defineClassType: defineClassType,
defineDuckType: defineDuckType,
defineExactDuckType: defineExactDuckType,
duckTypeFactory: duckTypeFactory,
exactDuckTypeFactory: exactDuckTypeFactory,
isRegisteredDuckType: isRegisteredDuckType,
reportDuckTypeErrors: reportDuckTypeErrors
};
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = signetDuckTypes;
}
function signetCoreTypes(
parser,
extend,
isTypeOf,
isSignetType,
isSignetSubtypeOf,
subtype,
alias,
defineDependentOperatorOn) {
'use strict';
function not(pred) {
return function (a, b) {
return !pred(a, b);
}
}
function compareSubsetProps(a, b) {
var keys = Object.keys(b);
var keyLength = keys.length;
var compareOk = true;
for (var i = 0; i < keyLength && compareOk; i++) {
var key = keys[i];
compareOk = a[key] === b[key];
}
return compareOk;
}
function objectsAreEqual(a, b) {
if (isNull(a) || isNull(b) || a === b) { return a === b; }
var objAKeys = Object.keys(a);
var keyLengthEqual = objAKeys.length === Object.keys(b).length;
return !keyLengthEqual ? false : compareSubsetProps(a, b);
}
function propertySuperSet(a, b) {
var keyLengthOk = !(Object.keys(a).length < Object.keys(b).length);
return keyLengthOk && compareSubsetProps(a, b);
}
function propertySubSet(a, b) {
return propertySuperSet(b, a);
}
function propertyCongruence(a, b) {
var keyLengthOk = Object.keys(a).length === Object.keys(b).length;
return keyLengthOk && compareSubsetProps(a, b);
}
function isSameType(a, b, aType, bType) {
var aTypeName = getVariantType(a, aType);
var bTypeName = getVariantType(b, bType);
return aTypeName === bTypeName;
}
function getVariantType(value, typeDef) {
return whichType(typeDef.subtype)(value);
}
function whichVariantType(variantString) {
var variantStrings = parser.parseType(variantString).subtype;
return whichType(variantStrings);
}
function find(predicate, values) {
var arrayLength = values.length;
var result = null;
for (var i = 0; i < arrayLength; i++) {
if (predicate(values[i])) {
result = values[i];
break;
}
}
return result;
}
function whichType(typeStrings) {
return function (value) {
function isMatch(typeString) {
return isTypeOf(typeString)(value);
}
var result = find(isMatch, typeStrings);
return typeof result !== 'string' ? null : result;
};
}
function isSubtypeOf(a, b, aType, bType) {
var aTypeName = getVariantType(a, aType);
var bTypeName = getVariantType(b, bType);
return isSignetSubtypeOf(bTypeName)(aTypeName);
}
function isSupertypeOf(a, b, aType, bType) {
return isSubtypeOf(b, a, bType, aType);
}
// function typeImplication(a, b, aType, bType) {
// }
function greater(a, b) {
return a > b;
}
function less(a, b) {
return a < b;
}
function equal(a, b) {
return a === b;
}
function isType(typeStr) {
return function (value) {
return typeof value === typeStr;
}
}
function isNull(value) {
return value === null;
}
function isFinite(value) {
return Math.abs(value) !== Infinity;
}
var isNaN = typeof Number.isNaN === 'undefined'
? function (value) { return value !== value; }
: function (value) { return Number.isNaN(value); };
function isNumber(value) {
return typeof value === 'number' && !isNaN(value);
}
function isBigInt (value) {
return typeof value === 'bigint' && !isNaN(value); // eslint-disable-line valid-typeof
}
function isNativeNumber (value) {
return isNumber(value) || isBigInt(value);
}
var checkNumberSubtype = isSignetSubtypeOf('nativeNumber');
function isNumberOrSubtype(typeName) {
return checkNumberSubtype(typeName);
}
var checkStringSubtype = isSignetSubtypeOf('string');
function isStringOrSubtype(typeName) {
return typeName === 'string' || checkStringSubtype(typeName);
}
var checkArraySubtype = isSignetSubtypeOf('array');
function isArrayOrSubtype(typeName) {
return typeName === 'array' || checkArraySubtype(typeName);
}
function getTypeFromTypeString(typeString) {
return parser.parseType(typeString).type;
}
function isSequence(value, options) {
var subtypeName = getTypeFromTypeString(options[0]);
if (!isNumberOrSubtype(subtypeName) && !isStringOrSubtype(subtypeName)) {
throw new Error('A sequence may only be comprised of numbers, strings or their subtypes.');
}
return checkArray(value, options);
}
function getMonotoneCompare(values) {
return greater(values[0], values[1]) ? greater : less
}
function checkMonotoneValues(values) {
var result = true;
var compare = getMonotoneCompare(values);
for (var i = 1; i < values.length; i++) {
result = result && compare(values[i - 1], values[i]);
}
return result;
}
function isMonotone(values, options) {
return isSequence(values, options) && checkMonotoneValues(values);
}
function isIncreasing(values, options) {
var firstValuesOk = values.length < 2 || less(values[0], values[1]);
return isMonotone(values, options) && firstValuesOk;
}
function isDecreasing(values, options) {
var firstValuesOk = values.length < 2 || greater(values[0], values[1]);
return isMonotone(values, options) && firstValuesOk;
}
function checkArrayValues(arrayValues, options) {
var result = true;
var checkType = isTypeOf(options[0]);
for (var i = 0; i < arrayValues.length; i++) {
result = checkType(arrayValues[i]);
if (!result) {
break;
}
}
return result;
}
function isArrayType(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
function checkArray(value, options) {
var checkValues = options.length > 0 && options[0] !== '*';
return isArrayType(value)
&& (!checkValues || checkArrayValues(value, options));
}
function isInt(value) {
return isBigInt(value) || checkInt(value);
}
function checkInt(value) {
return Math.floor(value) === value && value !== Infinity;
}
function isBounded(value, options) {
var typeName = getTypeFromTypeString(options[0]);
var range = optionsToRangeObject(options);
var isArrayOrString = isArrayOrSubtype(typeName) || isStringOrSubtype(typeName);
var isNumberType = isNumberOrSubtype(typeName);
var typeStringIsValid = isNumberType || isArrayOrString;
if (!typeStringIsValid) {
var errorMessage = 'Bounded type only accepts types of number, string, array or subtypes of these.'
throw new Error(errorMessage);
} else if (isTypeOf(options[0])(value)) {
var valueToCheck = isArrayOrString ? value.length : value;
return checkRange(valueToCheck, range);
} else {
return false;
}
}
function checkRange(value, range) {
return range.min <= value && value <= range.max;
}
function optionsToRangeObject(options) {
var range = {
min: Number(options[1]),
max: Number(options[2])
};
return range;
}
function optionsToRegex(options) {
return options.length === 1 ? options[0] : options.join(';');
}
function checkFormattedString(value, regex) {
return value.match(regex) !== null;
}
function optionsToFunctions(options) {
return options.map(isTypeOf);
}
function optionsToFunction(options) {
return options.join(', ');
}
function checkArgumentsObject(value) {
return !isNull(value);
}
function isRegExp(value) {
return Object.prototype.toString.call(value) === '[object RegExp]';
}
function compareTypes(typeA, typeB) {
var result = typeA === typeB ? 1 : 0;
return isSignetSubtypeOf(typeA)(typeB) ? -1 : result;
}
function insertTypeName(typeNameArray, typeName) {
var index = 0;
var offset = 0;
for (index; index < typeNameArray.length; index++) {
offset = compareTypes(typeNameArray[index], typeName);
if (offset !== 0) {
break;
}
}
typeNameArray.splice(index + offset, 0, typeName);
return typeNameArray;
}
function sortTypeNames(typeNames) {
return typeNames.reduce(insertTypeName, []);
}
function castOutOn(predicate, values) {
var result = false;
for (var i = 0; i < values.length; i++) {
result = predicate(values[i]);
if (result) {
values.splice(i, 1);
break;
}
}
return result;
}
function typeDoesNotExistIn(values) {
var valuesCopy = values.slice(0);
return function (typeName) {
var isTypeOfTypeName = isTypeOf(typeName);
return !castOutOn(isTypeOfTypeName, valuesCopy);
};
}
function reduce(action, values, initial) {
var arrayLength = values.length;
var result = initial;
for (var i = 0; i < arrayLength; i++) {
result = action(result, values[i]);
}
return result;
}
function filterOn(predicate) {
return function (result, value) {
if (predicate(value)) {
result.push(value);
}
return result;
}
}
function filter(predicate, values) {
return reduce(filterOn(predicate), values, []);
}
function checkValueTypes(values, typeNames) {
var sortedTypeNames = sortTypeNames(typeNames);
var filterResult = filter(typeDoesNotExistIn(values), sortedTypeNames);
return filterResult.length === 0;
}
function isUnorderedProduct(value, typeNames) {
var isCorrectLength = value.length === typeNames.length;
return isCorrectLength && checkValueTypes(value, typeNames);
}
function checkTuple(value, options) {
var lengthOkay = value.length === options.length;
return lengthOkay && options.reduce(verifyTupleTypes, true);
function verifyTupleTypes(result, validator, index) {
return result && validator(value[index]);
}
}
function isVariant(value, options) {
return options.length === 0
|| filter(checkValueType, options).length > 0;
function checkValueType(validator) {
return validator(value);
}
}
function checkTaggedUnion(value, options) {
console.warn('Tagged Union is deprecated, use variant instead.');
return isVariant(value, options);
}
function checkCompositeType(value, typePredicates) {
return typePredicates.reduce(function (result, predicate) {
return result && predicate(value);
}, true);
}
function checkNot(value, typePredicates) {
return !typePredicates[0](value);
}
function isRegisteredType(value) {
return typeof value === 'function' || isSignetType(parser.parseType(value).type);
}
function equalLength(a, b) {
return a.length === b.length;
}
function longer(a, b) {
return a.length > b.length;
}
function shorter(a, b) {
return a.length < b.length;
}
parser.registerTypeLevelMacro(function emptyParamsToStar(value) {
return /^\(\s*\)$/.test(value.trim()) ? '*' : value;
});
function buildTypePattern(macroPattern) {
var token = '{{typePattern}}';
var typePattern = '^([^\\:]+\\:)?(\\[)?' + token + '(\\])?$';
return new RegExp(typePattern.replace(token, macroPattern));
}
function matchAndReplace(value, pattern, replacement) {
return pattern.test(value) ? value.replace(pattern, replacement) : value;
}
parser.registerTypeLevelMacro(function bangStarDefinedValues(value) {
var pattern = buildTypePattern('(\\!\\*)');
var replacementStr = '$1$2not<variant<undefined, null>>$4';
return matchAndReplace(value.trim(), pattern, replacementStr);
});
parser.registerTypeLevelMacro(function questionMarkToOptionalType(value) {
var pattern = buildTypePattern('\\?([^\\]]*)');
var replacementStr = '$1$2variant<undefined, null, $3>$4';
return matchAndReplace(value.trim(), pattern, replacementStr);
});
parser.registerTypeLevelMacro(function caretToNot(value) {
var pattern = buildTypePattern('\\^([^\\]]*)');
var replacementStr = '$1$2not<$3>$4';
return matchAndReplace(value.trim(), pattern, replacementStr);
});
parser.registerSignatureLevelMacro(function signatureToFunction(value) {
var signaturePattern = /(\()((.*\=\>)+(.*))(\))/
var signatureMatch = signaturePattern.test(value);
return signatureMatch ? value.replace(signaturePattern, 'function<$2>') : value;
});
function checkSignatureMatch(fn, signature) {
return signature !== ''
? fn.signature === signature
: typeof fn.signature === 'string';
}
var enforcePattern = /enforceDecorator/ig;
function isEnforceFunction(fn) {
var fnString = Function.prototype.toString.call(fn);
return enforcePattern.test(fnString);
}
function isEnforcedFunction(value, options) {
var signature = typeof options !== 'undefined'
? options.join(',').trim()
: '';
var valueIsFunction = typeof value === 'function';
return valueIsFunction
&& isEnforceFunction(value)
&& checkSignatureMatch(value, signature);
}
function setDecimalPrecision(value, precision) {
var magnitude = Math.pow(10, precision);
return Math.floor(value * magnitude) / magnitude;
}
function checkDecimalPrecision(value, options) {
var precision = parseFloat(options[0]);
if (!checkInt(precision) || !checkRange(precision, { min: 0, max: Infinity })) {
throw new Error('Precision value must be of type leftBoundedInt<0>, but got: ' + precision + 'of type ' + typeof precision);
}
return value === setDecimalPrecision(value, precision);
}
extend('boolean{0}', isType('boolean'));
extend('function{0,1}', isType('function'), optionsToFunction);
extend('enforcedFunction{0,1}', isEnforcedFunction);
extend('nativeNumber', isNativeNumber);
extend('object{0}', isType('object'));
extend('string{0}', isType('string'));
extend('symbol{0}', isType('symbol'));
extend('undefined{0}', isType('undefined'));
extend('not{1}', checkNot, optionsToFunctions);
extend('null{0}', isNull);
extend('variant{1,}', isVariant, optionsToFunctions);
extend('taggedUnion{1,}', checkTaggedUnion, optionsToFunctions);
extend('composite{1,}', checkCompositeType, optionsToFunctions);
extend('bounded{3}', isBounded);
subtype('nativeNumber')('number{0}', isNumber);
subtype('nativeNumber')('bigint{0}', isBigInt);
subtype('nativeNumber')('int{0}', isInt);
subtype('object')('array{0,}', checkArray);
subtype('object')('regexp{0}', isRegExp);
subtype('nativeNumber')('finiteNumber', isFinite);
subtype('number')('decimalPrecision{1}', checkDecimalPrecision);
subtype('finiteNumber')('finiteInt{0}', checkInt);
subtype('string')('formattedString{1}', checkFormattedString, optionsToRegex);
subtype('array')('tuple{1,}', checkTuple, optionsToFunctions);
subtype('array')('unorderedProduct{1,}', isUnorderedProduct);
subtype('object')('arguments{0}', checkArgumentsObject);
subtype('array')('sequence{1}', isSequence);
subtype('array')('monotoneSequence{1}', isMonotone);
subtype('array')('increasingSequence{1}', isIncreasing);
subtype('array')('decreasingSequence{1}', isDecreasing);
alias('leftBounded', 'bounded<_, _, Infinity>');
alias('rightBounded', 'bounded<_, -Infinity, _>');
alias('boundedString{2}', 'bounded<string, _, _>');
alias('leftBoundedString{1}', 'leftBounded<string, _>');
alias('rightBoundedString{1}', 'rightBounded<string, _>');
alias('boundedNumber{2}', 'bounded<number, _, _>');
alias('leftBoundedNumber{1}', 'leftBounded<number, _>');
alias('rightBoundedNumber{1}', 'rightBounded<number, _>');
alias('boundedFiniteNumber{2}', 'bounded<finiteNumber, _, _>');
alias('leftBoundedFiniteNumber{1}', 'leftBounded<finiteNumber, _>');
alias('rightBoundedFiniteNumber{1}', 'rightBounded<finiteNumber, _>');
alias('boundedInt{2}', 'bounded<int, _, _>');
alias('leftBoundedInt{1}', 'leftBounded<int, _>');
alias('rightBoundedInt{1}', 'rightBounded<int, _>');
alias('boundedFiniteInt{2}', 'bounded<finiteInt, _, _>');
alias('leftBoundedFiniteInt{1}', 'leftBounded<finiteInt, _>');
alias('rightBoundedFiniteInt{1}', 'rightBounded<finiteInt, _>');
alias('typeValue{0}', 'variant<string, function>');
subtype('typeValue')('type{0}', isRegisteredType);
alias('any{0}', '*');
alias('void{0}', '*');
defineDependentOperatorOn('nativeNumber')('>', greater);
defineDependentOperatorOn('nativeNumber')('<', less);
defineDependentOperatorOn('nativeNumber')('=', equal);
defineDependentOperatorOn('nativeNumber')('>=', not(less));
defineDependentOperatorOn('nativeNumber')('<=', not(greater));
defineDependentOperatorOn('nativeNumber')('!=', not(equal));
defineDependentOperatorOn('int')('>', greater);
defineDependentOperatorOn