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

195 lines 7.78 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withOtherOptionSources = void 0; exports.withValidation = withValidation; exports.throwIfOptionIsUndefined = throwIfOptionIsUndefined; exports.parseOptionValueWithSchema = parseOptionValueWithSchema; /* eslint-disable no-underscore-dangle */ const joycon_1 = __importDefault(require("joycon")); /** * Processes default options behavior: * - removes `--` option as we never use it * - file options (e.g. hypertune.config.js, hypertune.json, hypertune key in package.json) * - environment variable options (e.g. HYPERTUNE_TOKEN) * - any options passed in to the returned handler (i.e. CLI args) */ // eslint-disable-next-line func-style const withOtherOptionSources = (handler, schema) => { // In ascending priority order // i.e. if an option is defined by wrapper[1], the same option defined by wrapper[0] will be ignored // Passing in arguments to the returned handler has the highest priority const wrappers = [ withoutDoubleDashOption, withFileOptions, withEnvVarOptions, ]; let wrapped = handler; for (const wrapper of wrappers) { wrapped = wrapper(wrapped, schema); } return wrapped; }; exports.withOtherOptionSources = withOtherOptionSources; // eslint-disable-next-line func-style const withoutDoubleDashOption = (handler) => { return (options) => { if (typeof options === "object" && options !== null && "--" in options) { // eslint-disable-next-line no-param-reassign delete options["--"]; } return handler(options); }; }; // eslint-disable-next-line func-style const withFileOptions = (handler) => { return (options) => __awaiter(void 0, void 0, void 0, function* () { const joycon = new joycon_1.default({ files: [ "hypertune.config.js", "hypertune.config.cjs", "hypertune.json", "package.json", ], packageKey: "hypertune", }); const res = yield joycon.load(); if (typeof res.data === "object" && res.data !== null) { return handler(mergeOptions(res.data, options)); } if (res.path) { console.warn(`Warning: Ignoring hypertune config at ${res.path}, as couldn't read it as a JavaScript object`); } return handler(options); }); }; // eslint-disable-next-line func-style const withEnvVarOptions = (handler, schema) => { return (options) => { // eslint-disable-next-line no-constant-binary-expression if (typeof process !== undefined && process.env) { const envOptions = Object.fromEntries(Object.entries(process.env) .filter(([k]) => /^(.*_)?HYPERTUNE_/.test(k)) .map(([k, v]) => [envNameToOptionName(k), v]) .map(([k, v]) => [ k, schema && schema[k] ? parseOptionValueWithSchema(schema[k], v) : v, ])); return handler(mergeOptions(envOptions, options)); } return handler(options); }; }; function mergeOptions(options, overridingOptions) { const commonKeys = Object.keys(options).filter((k) => k in overridingOptions); commonKeys.forEach((k) => { console.warn(`Warning: option "${String(k)}" defined multiple times, using value ${JSON.stringify(overridingOptions[k])}`); }); return Object.assign(Object.assign({}, options), overridingOptions); } /** * Adds options validation to a handler based on a Zod schema. * * As Zod schemas can specify defaults, also changes the input type to what is actually required (e.g. okay not to provide something where it has a default). */ function withValidation(schema, handler) { return (options) => { Object.entries(options).forEach(([option, value]) => { if (!(option in schema)) { console.warn(`Warning: Ignoring unrecognized option ${option}`); return; } switch (schema[option]) { case "any": return; case "string": if (typeof value !== "string") { throw new Error(`Option "${option}" must be a string, but ${typeof value} was provided`); } return; case "boolean": if (typeof value !== "boolean") { throw new Error(`Option "${option}" must be a boolean, but ${typeof value} was provided`); } return; case "number": if (typeof value !== "number") { throw new Error(`Option "${option}" must be a number, but ${typeof value} was provided`); } return; default: throw new Error(`Unexpected option type "${schema[option]}" for option "${option}"`); } }); return handler(options); }; } function throwIfOptionIsUndefined(optionName, value) { if (value === undefined) { throw new Error(`${optionName}: Missing required argument. Set it in your hypertune config (such as hypertune.json) as ${optionName}, use the ${optionNameToCliFlag(optionName)} argument, or the ${optionNameToEnvName(optionName)} environment variable.`); } return value; } function parseOptionValueWithSchema(valueType, value) { switch (valueType) { case "boolean": { switch (value.toLowerCase().trim()) { case "1": case "yes": case "true": return true; case "0": case "no": case "false": return false; default: return value; } } case "number": { if (/^-?\d+\.?\d*$/.test(value)) { return Number(value); } return value; } default: return value; } } /** * @example envNameToOptionName("NEXT_PUBLIC_HYPERTUNE_OUTPUT_FILE_PATH") == "outputFilePath" */ function envNameToOptionName(envName) { const afterPrefix = envName.replace(/^(.*?_?)HYPERTUNE_/, ""); return afterPrefix .toLowerCase() .replace(/_+(.)/g, (_, char) => char.toUpperCase()); } /** * @example optionNameToEnvName("outputFilePath") == "HYPERTUNE_OUTPUT_FILE_PATH" */ function optionNameToEnvName(optionName) { return `HYPERTUNE_${optionName .replace(/[A-Z]/g, (letter) => `_${letter}`) .toUpperCase()}`; } /** * @example optionNameToCliFlag("outputFilePath") == "--outputFilePath" */ function optionNameToCliFlag(optionName) { return `--${optionName}`; } //# sourceMappingURL=helpers.js.map