UNPKG

hypertune

Version:

[Hypertune](https://www.hypertune.com/) is the most flexible platform for feature flags, A/B testing, analytics and app configuration. Built with full end-to-end type-safety, Git-style version control and local, synchronous, in-memory flag evaluation. Opt

238 lines 14.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = fold; exports.mapExpressionWithResult = mapExpressionWithResult; exports.mapExpression = mapExpression; // Expensive function fold(f, expression) { if (!expression) { return f(expression); } switch (expression.type) { case "NoOpExpression": case "BooleanExpression": case "IntExpression": case "FloatExpression": case "StringExpression": case "RegexExpression": case "EnumExpression": return f(expression); case "ObjectExpression": return f(Object.assign(Object.assign({}, expression), { fields: Object.fromEntries(Object.entries(expression.fields).map(([fieldName, field]) => [ fieldName, fold(f, field), ])) })); case "GetFieldExpression": return f(Object.assign(Object.assign({}, expression), { object: fold(f, expression.object) })); case "UpdateObjectExpression": return f(Object.assign(Object.assign({}, expression), { object: fold(f, expression.object), updates: Object.fromEntries(Object.entries(expression.updates).map(([fieldName, field]) => [ fieldName, fold(f, field), ])) })); case "ListExpression": return f(Object.assign(Object.assign({}, expression), { items: expression.items.map((item) => fold(f, item)) })); case "SwitchExpression": return f(Object.assign(Object.assign({}, expression), { control: fold(f, expression.control), cases: expression.cases.map((item) => ({ id: item.id, when: fold(f, item.when), then: fold(f, item.then), })), default: fold(f, expression.default) })); case "EnumSwitchExpression": return f(Object.assign(Object.assign({}, expression), { control: fold(f, expression.control), cases: Object.fromEntries(Object.entries(expression.cases).map(([enumValue, caseExpression]) => [ enumValue, fold(f, caseExpression), ])) })); case "ComparisonExpression": case "ArithmeticExpression": return f(Object.assign(Object.assign({}, expression), { a: fold(f, expression.a), b: fold(f, expression.b) })); case "RoundNumberExpression": case "StringifyNumberExpression": return f(Object.assign(Object.assign({}, expression), { number: fold(f, expression.number) })); case "StringConcatExpression": return f(Object.assign(Object.assign({}, expression), { strings: fold(f, expression.strings) })); case "GetUrlQueryParameterExpression": return f(Object.assign(Object.assign({}, expression), { url: fold(f, expression.url), queryParameterName: fold(f, expression.queryParameterName) })); case "SplitExpression": return f(Object.assign(Object.assign({}, expression), { expose: fold(f, expression.expose), unitId: fold(f, expression.unitId), dimensionMapping: expression.dimensionMapping.type === "discrete" ? { type: "discrete", cases: Object.fromEntries(Object.entries(expression.dimensionMapping.cases).map(([armId, caseExpression]) => [ armId, fold(f, caseExpression), ])), } : { type: "continuous", function: fold(f, expression.dimensionMapping.function), }, eventObjectTypeName: expression.eventObjectTypeName, eventPayload: fold(f, expression.eventPayload) })); case "LogEventExpression": return f(Object.assign(Object.assign({}, expression), { unitId: fold(f, expression.unitId), eventPayload: fold(f, expression.eventPayload) })); case "FunctionExpression": return f(Object.assign(Object.assign({}, expression), { body: fold(f, expression.body) })); case "VariableExpression": return f(expression); case "ApplicationExpression": return f(Object.assign(Object.assign({}, expression), { function: fold(f, expression.function), arguments: expression.arguments.map((argument) => fold(f, argument)) })); default: { const neverExpression = expression; throw new Error(`Unexpected expression: ${JSON.stringify(neverExpression)}`); } } } // Applies f to children, constructs new expression, applies f to it and returns // it along with merged map results // Expensive function mapExpressionWithResult(fn, combineResults, expression) { // eslint-disable-next-line func-style const foldFunction = (partialResult) => { if (!partialResult) { return fn(partialResult); } switch (partialResult.type) { case "NoOpExpression": case "BooleanExpression": case "IntExpression": case "FloatExpression": case "StringExpression": case "RegexExpression": case "EnumExpression": return fn(partialResult); case "ObjectExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { fields: Object.fromEntries(Object.entries(partialResult.fields).map(([fieldName, fieldResult]) => [ fieldName, fieldResult.newExpression, ])) })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, ...Object.values(partialResult.fields).map((fieldResult) => fieldResult.mapResult)), }; } case "GetFieldExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { object: partialResult.object.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.object.mapResult), }; } case "UpdateObjectExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { object: partialResult.object.newExpression, updates: Object.fromEntries(Object.entries(partialResult.updates).map(([fieldName, updateResult]) => [ fieldName, updateResult.newExpression, ])) })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.object.mapResult, ...Object.values(partialResult.updates).map((updateResult) => updateResult.mapResult)), }; } case "ListExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { items: partialResult.items.map((itemResult) => itemResult.newExpression) })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, ...partialResult.items.map((itemResult) => itemResult.mapResult)), }; } case "SwitchExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { control: partialResult.control.newExpression, cases: partialResult.cases.map((caseResult) => ({ id: caseResult.id, when: caseResult.when.newExpression, then: caseResult.then.newExpression, })), default: partialResult.default.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.control.mapResult, ...partialResult.cases.map((caseResult) => combineResults(caseResult.when.mapResult, caseResult.then.mapResult)), partialResult.default.mapResult), }; } case "EnumSwitchExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { control: partialResult.control.newExpression, cases: Object.fromEntries(Object.entries(partialResult.cases).map(([enumValue, caseResult]) => [enumValue, caseResult.newExpression])) })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.control.mapResult, ...Object.values(partialResult.cases).map((caseResult) => caseResult.mapResult)), }; } case "ComparisonExpression": case "ArithmeticExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { a: partialResult.a.newExpression, b: partialResult.b.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.a.mapResult, partialResult.b.mapResult), }; } case "RoundNumberExpression": case "StringifyNumberExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { number: partialResult.number.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.number.mapResult), }; } case "StringConcatExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { strings: partialResult.strings.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.strings.mapResult), }; } case "GetUrlQueryParameterExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { url: partialResult.url.newExpression, queryParameterName: partialResult.queryParameterName.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.url.mapResult, partialResult.queryParameterName.mapResult), }; } case "SplitExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { expose: partialResult.expose.newExpression, unitId: partialResult.unitId.newExpression, dimensionMapping: partialResult.dimensionMapping.type === "discrete" ? { type: "discrete", cases: Object.fromEntries(Object.entries(partialResult.dimensionMapping.cases).map(([armId, caseResult]) => [armId, caseResult.newExpression])), } : { type: "continuous", function: partialResult.dimensionMapping.function.newExpression, }, eventObjectTypeName: partialResult.eventObjectTypeName, eventPayload: partialResult.eventPayload.newExpression, featuresMapping: {} })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.expose.mapResult, partialResult.unitId.mapResult, partialResult.eventPayload.mapResult, ...(partialResult.dimensionMapping.type === "discrete" ? Object.values(partialResult.dimensionMapping.cases).map((caseMapResult) => caseMapResult.mapResult) : [partialResult.dimensionMapping.function.mapResult])), }; } case "LogEventExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { eventObjectTypeName: partialResult.eventObjectTypeName, eventPayload: partialResult.eventPayload.newExpression, unitId: partialResult.unitId.newExpression, featuresMapping: {} })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.unitId.mapResult, partialResult.eventPayload.mapResult), }; } case "FunctionExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { body: partialResult.body.newExpression })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.body.mapResult), }; } case "VariableExpression": return fn(partialResult); case "ApplicationExpression": { const thisResult = fn(Object.assign(Object.assign({}, partialResult), { function: partialResult.function.newExpression, arguments: partialResult.arguments.map((argumentResult) => argumentResult.newExpression) })); return { newExpression: thisResult.newExpression, mapResult: combineResults(thisResult.mapResult, partialResult.function.mapResult, ...partialResult.arguments.map((argumentResult) => argumentResult.mapResult)), }; } default: { const neverPartialResult = partialResult; throw new Error(`Unexpected partial result: ${JSON.stringify(neverPartialResult)}`); } } }; return fold(foldFunction, expression); } // Expensive function mapExpression(mapper, expression) { const result = mapExpressionWithResult((expr) => ({ newExpression: mapper(expr), mapResult: null, }), () => null, expression); return result.newExpression; } //# sourceMappingURL=fold.js.map