UNPKG

veffect

Version:

powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha

467 lines (466 loc) 21.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.within = exports.upperCase = exports.unnested = exports.snakeCase = exports.orElse = exports.nested = exports.mapInputPath = exports.makeFlat = exports.make = exports.lowerCase = exports.kebabCase = exports.fromMap = exports.fromJson = exports.fromFlat = exports.fromEnv = exports.constantCase = exports.configProviderTag = exports.FlatConfigProviderTypeId = exports.ConfigProviderTypeId = void 0; var Context = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Context.js")); var Either = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Either.js")); var _Function = /*#__PURE__*/require("../Function.js"); var HashMap = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../HashMap.js")); var HashSet = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../HashSet.js")); var number = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Number.js")); var Option = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Option.js")); var _Pipeable = /*#__PURE__*/require("../Pipeable.js"); var ReadonlyArray = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../ReadonlyArray.js")); var regexp = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../RegExp.js")); var configError = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./configError.js")); var pathPatch = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./configProvider/pathPatch.js")); var core = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./core.js")); var OpCodes = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./opCodes/config.js")); var StringUtils = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./string-utils.js")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const concat = (l, r) => [...l, ...r]; /** @internal */ const ConfigProviderSymbolKey = "effect/ConfigProvider"; /** @internal */ const ConfigProviderTypeId = exports.ConfigProviderTypeId = /*#__PURE__*/Symbol.for(ConfigProviderSymbolKey); /** @internal */ const configProviderTag = exports.configProviderTag = /*#__PURE__*/Context.GenericTag("effect/ConfigProvider"); /** @internal */ const FlatConfigProviderSymbolKey = "effect/ConfigProviderFlat"; /** @internal */ const FlatConfigProviderTypeId = exports.FlatConfigProviderTypeId = /*#__PURE__*/Symbol.for(FlatConfigProviderSymbolKey); /** @internal */ const make = options => ({ [ConfigProviderTypeId]: ConfigProviderTypeId, pipe() { return (0, _Pipeable.pipeArguments)(this, arguments); }, ...options }); /** @internal */ exports.make = make; const makeFlat = options => ({ [FlatConfigProviderTypeId]: FlatConfigProviderTypeId, patch: options.patch, load: (path, config, split = true) => options.load(path, config, split), enumerateChildren: options.enumerateChildren }); /** @internal */ exports.makeFlat = makeFlat; const fromFlat = flat => make({ load: config => core.flatMap(fromFlatLoop(flat, ReadonlyArray.empty(), config, false), chunk => Option.match(ReadonlyArray.head(chunk), { onNone: () => core.fail(configError.MissingData(ReadonlyArray.empty(), `Expected a single value having structure: ${config}`)), onSome: core.succeed })), flattened: flat }); /** @internal */ exports.fromFlat = fromFlat; const fromEnv = config => { const { pathDelim, seqDelim } = Object.assign({}, { pathDelim: "_", seqDelim: "," }, config); const makePathString = path => (0, _Function.pipe)(path, ReadonlyArray.join(pathDelim)); const unmakePathString = pathString => pathString.split(pathDelim); const getEnv = () => typeof process !== "undefined" && "env" in process && typeof process.env === "object" ? process.env : {}; const load = (path, primitive, split = true) => { const pathString = makePathString(path); const current = getEnv(); const valueOpt = pathString in current ? Option.some(current[pathString]) : Option.none(); return (0, _Function.pipe)(valueOpt, core.mapError(() => configError.MissingData(path, `Expected ${pathString} to exist in the process context`)), core.flatMap(value => parsePrimitive(value, path, primitive, seqDelim, split))); }; const enumerateChildren = path => core.sync(() => { const current = getEnv(); const keys = Object.keys(current); const keyPaths = Array.from(keys).map(value => unmakePathString(value.toUpperCase())); const filteredKeyPaths = keyPaths.filter(keyPath => { for (let i = 0; i < path.length; i++) { const pathComponent = (0, _Function.pipe)(path, ReadonlyArray.unsafeGet(i)); const currentElement = keyPath[i]; if (currentElement === undefined || pathComponent !== currentElement) { return false; } } return true; }).flatMap(keyPath => keyPath.slice(path.length, path.length + 1)); return HashSet.fromIterable(filteredKeyPaths); }); return fromFlat(makeFlat({ load, enumerateChildren, patch: pathPatch.empty })); }; /** @internal */ exports.fromEnv = fromEnv; const fromMap = (map, config) => { const { pathDelim, seqDelim } = Object.assign({ seqDelim: ",", pathDelim: "." }, config); const makePathString = path => (0, _Function.pipe)(path, ReadonlyArray.join(pathDelim)); const unmakePathString = pathString => pathString.split(pathDelim); const mapWithIndexSplit = splitIndexInKeys(map, str => Array.from(unmakePathString(str)), makePathString); const load = (path, primitive, split = true) => { const pathString = makePathString(path); const valueOpt = mapWithIndexSplit.has(pathString) ? Option.some(mapWithIndexSplit.get(pathString)) : Option.none(); return (0, _Function.pipe)(valueOpt, core.mapError(() => configError.MissingData(path, `Expected ${pathString} to exist in the provided map`)), core.flatMap(value => parsePrimitive(value, path, primitive, seqDelim, split))); }; const enumerateChildren = path => core.sync(() => { const keyPaths = Array.from(mapWithIndexSplit.keys()).map(unmakePathString); const filteredKeyPaths = keyPaths.filter(keyPath => { for (let i = 0; i < path.length; i++) { const pathComponent = (0, _Function.pipe)(path, ReadonlyArray.unsafeGet(i)); const currentElement = keyPath[i]; if (currentElement === undefined || pathComponent !== currentElement) { return false; } } return true; }).flatMap(keyPath => keyPath.slice(path.length, path.length + 1)); return HashSet.fromIterable(filteredKeyPaths); }); return fromFlat(makeFlat({ load, enumerateChildren, patch: pathPatch.empty })); }; exports.fromMap = fromMap; const extend = (leftDef, rightDef, left, right) => { const leftPad = ReadonlyArray.unfold(left.length, index => index >= right.length ? Option.none() : Option.some([leftDef(index), index + 1])); const rightPad = ReadonlyArray.unfold(right.length, index => index >= left.length ? Option.none() : Option.some([rightDef(index), index + 1])); const leftExtension = concat(left, leftPad); const rightExtension = concat(right, rightPad); return [leftExtension, rightExtension]; }; const appendConfigPath = (path, config) => { let op = config; if (op._tag === "Nested") { const out = path.slice(); while (op._tag === "Nested") { out.push(op.name); op = op.config; } return out; } return path; }; const fromFlatLoop = (flat, prefix, config, split) => { const op = config; switch (op._tag) { case OpCodes.OP_CONSTANT: { return core.succeed(ReadonlyArray.of(op.value)); } case OpCodes.OP_DESCRIBED: { return core.suspend(() => fromFlatLoop(flat, prefix, op.config, split)); } case OpCodes.OP_FAIL: { return core.fail(configError.MissingData(prefix, op.message)); } case OpCodes.OP_FALLBACK: { return (0, _Function.pipe)(core.suspend(() => fromFlatLoop(flat, prefix, op.first, split)), core.catchAll(error1 => { if (op.condition(error1)) { return (0, _Function.pipe)(fromFlatLoop(flat, prefix, op.second, split), core.catchAll(error2 => core.fail(configError.Or(error1, error2)))); } return core.fail(error1); })); } case OpCodes.OP_LAZY: { return core.suspend(() => fromFlatLoop(flat, prefix, op.config(), split)); } case OpCodes.OP_MAP_OR_FAIL: { return core.suspend(() => (0, _Function.pipe)(fromFlatLoop(flat, prefix, op.original, split), core.flatMap(core.forEachSequential(a => (0, _Function.pipe)(op.mapOrFail(a), core.mapError(configError.prefixed(appendConfigPath(prefix, op.original)))))))); } case OpCodes.OP_NESTED: { return core.suspend(() => fromFlatLoop(flat, concat(prefix, ReadonlyArray.of(op.name)), op.config, split)); } case OpCodes.OP_PRIMITIVE: { return (0, _Function.pipe)(pathPatch.patch(prefix, flat.patch), core.flatMap(prefix => (0, _Function.pipe)(flat.load(prefix, op, split), core.flatMap(values => { if (values.length === 0) { const name = (0, _Function.pipe)(ReadonlyArray.last(prefix), Option.getOrElse(() => "<n/a>")); return core.fail(configError.MissingData([], `Expected ${op.description} with name ${name}`)); } return core.succeed(values); })))); } case OpCodes.OP_SEQUENCE: { return (0, _Function.pipe)(pathPatch.patch(prefix, flat.patch), core.flatMap(patchedPrefix => (0, _Function.pipe)(flat.enumerateChildren(patchedPrefix), core.flatMap(indicesFrom), core.flatMap(indices => { if (indices.length === 0) { return core.suspend(() => core.map(fromFlatLoop(flat, patchedPrefix, op.config, true), ReadonlyArray.of)); } return (0, _Function.pipe)(core.forEachSequential(indices, index => fromFlatLoop(flat, ReadonlyArray.append(prefix, `[${index}]`), op.config, true)), core.map(chunkChunk => { const flattened = ReadonlyArray.flatten(chunkChunk); if (flattened.length === 0) { return ReadonlyArray.of(ReadonlyArray.empty()); } return ReadonlyArray.of(flattened); })); })))); } case OpCodes.OP_HASHMAP: { return core.suspend(() => (0, _Function.pipe)(pathPatch.patch(prefix, flat.patch), core.flatMap(prefix => (0, _Function.pipe)(flat.enumerateChildren(prefix), core.flatMap(keys => { return (0, _Function.pipe)(keys, core.forEachSequential(key => fromFlatLoop(flat, concat(prefix, ReadonlyArray.of(key)), op.valueConfig, split)), core.map(values => { if (values.length === 0) { return ReadonlyArray.of(HashMap.empty()); } const matrix = values.map(x => Array.from(x)); return (0, _Function.pipe)(transpose(matrix), ReadonlyArray.map(values => HashMap.fromIterable(ReadonlyArray.zip(ReadonlyArray.fromIterable(keys), values)))); })); }))))); } case OpCodes.OP_ZIP_WITH: { return core.suspend(() => (0, _Function.pipe)(fromFlatLoop(flat, prefix, op.left, split), core.either, core.flatMap(left => (0, _Function.pipe)(fromFlatLoop(flat, prefix, op.right, split), core.either, core.flatMap(right => { if (Either.isLeft(left) && Either.isLeft(right)) { return core.fail(configError.And(left.left, right.left)); } if (Either.isLeft(left) && Either.isRight(right)) { return core.fail(left.left); } if (Either.isRight(left) && Either.isLeft(right)) { return core.fail(right.left); } if (Either.isRight(left) && Either.isRight(right)) { const path = (0, _Function.pipe)(prefix, ReadonlyArray.join(".")); const fail = fromFlatLoopFail(prefix, path); const [lefts, rights] = extend(fail, fail, (0, _Function.pipe)(left.right, ReadonlyArray.map(Either.right)), (0, _Function.pipe)(right.right, ReadonlyArray.map(Either.right))); return (0, _Function.pipe)(lefts, ReadonlyArray.zip(rights), core.forEachSequential(([left, right]) => (0, _Function.pipe)(core.zip(left, right), core.map(([left, right]) => op.zip(left, right))))); } throw new Error("BUG: ConfigProvider.fromFlatLoop - please report an issue at https://github.com/Effect-TS/effect/issues"); }))))); } } }; const fromFlatLoopFail = (prefix, path) => index => Either.left(configError.MissingData(prefix, `The element at index ${index} in a sequence at path "${path}" was missing`)); /** @internal */ const mapInputPath = exports.mapInputPath = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => fromFlat(mapInputPathFlat(self.flattened, f))); const mapInputPathFlat = (self, f) => makeFlat({ load: (path, config, split = true) => self.load(path, config, split), enumerateChildren: path => self.enumerateChildren(path), patch: pathPatch.mapName(self.patch, f) }); /** @internal */ const nested = exports.nested = /*#__PURE__*/(0, _Function.dual)(2, (self, name) => fromFlat(makeFlat({ load: (path, config) => self.flattened.load(path, config, true), enumerateChildren: path => self.flattened.enumerateChildren(path), patch: pathPatch.nested(self.flattened.patch, name) }))); /** @internal */ const unnested = exports.unnested = /*#__PURE__*/(0, _Function.dual)(2, (self, name) => fromFlat(makeFlat({ load: (path, config) => self.flattened.load(path, config, true), enumerateChildren: path => self.flattened.enumerateChildren(path), patch: pathPatch.unnested(self.flattened.patch, name) }))); /** @internal */ const orElse = exports.orElse = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => fromFlat(orElseFlat(self.flattened, () => that().flattened))); const orElseFlat = (self, that) => makeFlat({ load: (path, config, split) => (0, _Function.pipe)(pathPatch.patch(path, self.patch), core.flatMap(patch => self.load(patch, config, split)), core.catchAll(error1 => (0, _Function.pipe)(core.sync(that), core.flatMap(that => (0, _Function.pipe)(pathPatch.patch(path, that.patch), core.flatMap(patch => that.load(patch, config, split)), core.catchAll(error2 => core.fail(configError.Or(error1, error2)))))))), enumerateChildren: path => (0, _Function.pipe)(pathPatch.patch(path, self.patch), core.flatMap(patch => self.enumerateChildren(patch)), core.either, core.flatMap(left => (0, _Function.pipe)(core.sync(that), core.flatMap(that => (0, _Function.pipe)(pathPatch.patch(path, that.patch), core.flatMap(patch => that.enumerateChildren(patch)), core.either, core.flatMap(right => { if (Either.isLeft(left) && Either.isLeft(right)) { return core.fail(configError.And(left.left, right.left)); } if (Either.isLeft(left) && Either.isRight(right)) { return core.succeed(right.right); } if (Either.isRight(left) && Either.isLeft(right)) { return core.succeed(left.right); } if (Either.isRight(left) && Either.isRight(right)) { return core.succeed((0, _Function.pipe)(left.right, HashSet.union(right.right))); } throw new Error("BUG: ConfigProvider.orElseFlat - please report an issue at https://github.com/Effect-TS/effect/issues"); })))))), patch: pathPatch.empty }); /** @internal */ const constantCase = self => mapInputPath(self, StringUtils.constantCase); /** @internal */ exports.constantCase = constantCase; const kebabCase = self => mapInputPath(self, StringUtils.kebabCase); /** @internal */ exports.kebabCase = kebabCase; const lowerCase = self => mapInputPath(self, StringUtils.lowerCase); /** @internal */ exports.lowerCase = lowerCase; const snakeCase = self => mapInputPath(self, StringUtils.snakeCase); /** @internal */ exports.snakeCase = snakeCase; const upperCase = self => mapInputPath(self, StringUtils.upperCase); /** @internal */ exports.upperCase = upperCase; const within = exports.within = /*#__PURE__*/(0, _Function.dual)(3, (self, path, f) => { const unnest = ReadonlyArray.reduce(path, self, (provider, name) => unnested(provider, name)); const nest = ReadonlyArray.reduceRight(path, f(unnest), (provider, name) => nested(provider, name)); return orElse(nest, () => self); }); const splitPathString = (text, delim) => { const split = text.split(new RegExp(`\\s*${regexp.escape(delim)}\\s*`)); return split; }; const parsePrimitive = (text, path, primitive, delimiter, split) => { if (!split) { return (0, _Function.pipe)(primitive.parse(text), core.mapBoth({ onFailure: configError.prefixed(path), onSuccess: ReadonlyArray.of })); } return (0, _Function.pipe)(splitPathString(text, delimiter), core.forEachSequential(char => primitive.parse(char.trim())), core.mapError(configError.prefixed(path))); }; const transpose = array => { return Object.keys(array[0]).map(column => array.map(row => row[column])); }; const indicesFrom = quotedIndices => (0, _Function.pipe)(core.forEachSequential(quotedIndices, parseQuotedIndex), core.mapBoth({ onFailure: () => ReadonlyArray.empty(), onSuccess: ReadonlyArray.sort(number.Order) }), core.either, core.map(Either.merge)); const STR_INDEX_REGEX = /(^.+)(\[(\d+)\])$/; const QUOTED_INDEX_REGEX = /^(\[(\d+)\])$/; const parseQuotedIndex = str => { const match = str.match(QUOTED_INDEX_REGEX); if (match !== null) { const matchedIndex = match[2]; return (0, _Function.pipe)(matchedIndex !== undefined && matchedIndex.length > 0 ? Option.some(matchedIndex) : Option.none(), Option.flatMap(parseInteger)); } return Option.none(); }; const splitIndexInKeys = (map, unmakePathString, makePathString) => { const newMap = new Map(); for (const [pathString, value] of map) { const keyWithIndex = (0, _Function.pipe)(unmakePathString(pathString), ReadonlyArray.flatMap(key => Option.match(splitIndexFrom(key), { onNone: () => ReadonlyArray.of(key), onSome: ([key, index]) => ReadonlyArray.make(key, `[${index}]`) }))); newMap.set(makePathString(keyWithIndex), value); } return newMap; }; const splitIndexFrom = key => { const match = key.match(STR_INDEX_REGEX); if (match !== null) { const matchedString = match[1]; const matchedIndex = match[3]; const optionalString = matchedString !== undefined && matchedString.length > 0 ? Option.some(matchedString) : Option.none(); const optionalIndex = (0, _Function.pipe)(matchedIndex !== undefined && matchedIndex.length > 0 ? Option.some(matchedIndex) : Option.none(), Option.flatMap(parseInteger)); return Option.all([optionalString, optionalIndex]); } return Option.none(); }; const parseInteger = str => { const parsedIndex = Number.parseInt(str); return Number.isNaN(parsedIndex) ? Option.none() : Option.some(parsedIndex); }; const keyName = name => ({ _tag: "KeyName", name }); const keyIndex = index => ({ _tag: "KeyIndex", index }); /** @internal */ const fromJson = json => { const hiddenDelimiter = "\ufeff"; const indexedEntries = ReadonlyArray.map(getIndexedEntries(json), ([key, value]) => [configPathToString(key).join(hiddenDelimiter), value]); return fromMap(new Map(indexedEntries), { pathDelim: hiddenDelimiter, seqDelim: hiddenDelimiter }); }; exports.fromJson = fromJson; const configPathToString = path => { const output = []; let i = 0; while (i < path.length) { const component = path[i]; if (component._tag === "KeyName") { if (i + 1 < path.length) { const nextComponent = path[i + 1]; if (nextComponent._tag === "KeyIndex") { output.push(`${component.name}[${nextComponent.index}]`); i += 2; } else { output.push(component.name); i += 1; } } else { output.push(component.name); i += 1; } } } return output; }; const getIndexedEntries = config => { const loopAny = (path, value) => { if (typeof value === "string") { return ReadonlyArray.make([path, value]); } if (typeof value === "number" || typeof value === "boolean") { return ReadonlyArray.make([path, String(value)]); } if (Array.isArray(value)) { return loopArray(path, value); } if (typeof value === "object" && value !== null) { return loopObject(path, value); } return ReadonlyArray.empty(); }; const loopArray = (path, values) => ReadonlyArray.match(values, { onEmpty: () => ReadonlyArray.make([path, "<nil>"]), onNonEmpty: ReadonlyArray.flatMap((value, index) => loopAny(ReadonlyArray.append(path, keyIndex(index)), value)) }); const loopObject = (path, value) => Object.entries(value).flatMap(([key, value]) => { const newPath = ReadonlyArray.append(path, keyName(key)); const result = loopAny(newPath, value); if (ReadonlyArray.isEmptyReadonlyArray(result)) { return ReadonlyArray.make([newPath, ""]); } return result; }); return loopObject(ReadonlyArray.empty(), config); }; //# sourceMappingURL=configProvider.js.map