UNPKG

@configu/sdk

Version:

Configu TypeScript SDK

1,033 lines (1,019 loc) 36.2 kB
'use strict'; var _9 = require('lodash'); var Ajv = require('ajv'); var addFormats = require('ajv-formats'); var ajvHumanErrors = require('@segment/ajv-human-errors'); var acorn = require('acorn'); var walk = require('acorn-walk'); require('ses'); var validator = require('validator'); var zod = require('zod'); var chai = require('chai'); var Subset = require('chai-subset'); var expect = require('@vitest/expect'); var matchers = require('jest-extended'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var _9__default = /*#__PURE__*/_interopDefault(_9); var Ajv__default = /*#__PURE__*/_interopDefault(Ajv); var addFormats__default = /*#__PURE__*/_interopDefault(addFormats); var acorn__namespace = /*#__PURE__*/_interopNamespace(acorn); var walk__namespace = /*#__PURE__*/_interopNamespace(walk); var validator__default = /*#__PURE__*/_interopDefault(validator); var chai__namespace = /*#__PURE__*/_interopNamespace(chai); var Subset__default = /*#__PURE__*/_interopDefault(Subset); var matchers__namespace = /*#__PURE__*/_interopNamespace(matchers); var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var ConfigKey = class _ConfigKey { static { __name(this, "ConfigKey"); } static pattern = "^[A-Za-z0-9_-]+$"; static reserved = [ "_", "-", "$", "this", "Cfgu", "ConfigStore", "ConfigSet", "ConfigKey", "Key", "ConfigValue", "Value", "ConfigSchema", "Configu" ]; static normalizedReserved = _ConfigKey.reserved.map(_ConfigKey.normalize); static normalize(key) { return _9__default.default.camelCase(key).toLowerCase(); } static validate({ key, errorPrefix = "ConfigKey" }) { try { const isValid = RegExp(_ConfigKey.pattern).test(key) && !_ConfigKey.normalizedReserved.includes(_ConfigKey.normalize(key)); if (!isValid) { throw new Error(); } } catch (error) { throw new Error( `${errorPrefix} "${key}" must match ${_ConfigKey.pattern} and not be one of ${_ConfigKey.reserved.join(", ")}` ); } } }; var JSONSchema = class _JSONSchema { static { __name(this, "JSONSchema"); } static ajv = addFormats__default.default( new Ajv__default.default({ allErrors: true, verbose: true, allowUnionTypes: true, useDefaults: "empty" }) ); static validate(schema, data) { const isValid = _JSONSchema.ajv.validate(schema, data); if (!isValid) { const errors = new ajvHumanErrors.AggregateAjvError(_JSONSchema.ajv.errors); throw new Error(errors.message); } } static AnyPropertySchema = { type: ["string", "number", "boolean", "object", "array"], oneOf: [{ type: "string" }, { type: "number" }, { type: "boolean" }, { type: "object" }, { type: "array" }] }; static AnyArrayPropertySchema = { type: "array", uniqueItems: true, items: _JSONSchema.AnyPropertySchema }; static createPropertyExclusiveSchema({ property, exclusive }) { return { if: { required: [property] }, then: { properties: { ..._9__default.default.chain(exclusive).keyBy().mapValues(() => false).value() } } }; } }; // src/Cfgu.ts var CfguStringProperty = { type: "string", minLength: 1 }; var CfguBooleanProperty = { type: "boolean" }; var CfguObjectProperty = { type: "object", minProperties: 1 }; var CfguStringArrayProperty = { type: "array", minItems: 1, uniqueItems: true, items: CfguStringProperty }; var CfguStringOrStringArrayProperty = { type: ["string", "array"], oneOf: [CfguStringProperty, CfguStringArrayProperty] }; var CfguSchema = { type: "object", required: [], additionalProperties: false, nullable: true, properties: { description: CfguStringProperty, label: CfguStringOrStringArrayProperty, hidden: CfguBooleanProperty, lazy: CfguBooleanProperty, const: CfguStringProperty, default: JSONSchema.AnyPropertySchema, required: CfguBooleanProperty, pattern: CfguStringProperty, enum: JSONSchema.AnyArrayPropertySchema, schema: CfguObjectProperty, test: CfguStringOrStringArrayProperty }, allOf: [ JSONSchema.createPropertyExclusiveSchema({ property: "lazy", exclusive: ["const", "default"] }), JSONSchema.createPropertyExclusiveSchema({ property: "const", // todo: consider if its a stupid but valid pair with required // exclusive: ['lazy', 'default'] exclusive: ["lazy", "default", "required"] }), JSONSchema.createPropertyExclusiveSchema({ property: "default", exclusive: ["lazy", "const", "required"] }), JSONSchema.createPropertyExclusiveSchema({ property: "required", exclusive: ["const", "default"] }) ] }; chai__namespace.use(expect.JestExtend); chai__namespace.use(expect.JestChaiExpect); chai__namespace.use(Subset__default.default); chai__namespace.use(expect.JestAsymmetricMatchers); chai__namespace.expect.extend(chai__namespace.expect, matchers__namespace); // src/ConfigExpression.ts var ConfigExpression = class _ConfigExpression { static { __name(this, "ConfigExpression"); } static globals = /* @__PURE__ */ new Map(); static suffix = "Expression"; static marker = { start: "${", end: "}" }; static delimiters = [ { start: "{{", end: "}}" }, { start: "<%", end: "%>" } ]; static { _ConfigExpression.register("_", _9__default.default); _ConfigExpression.register("validator", validator__default.default); _ConfigExpression.register("JSONSchema", JSONSchema); _ConfigExpression.register("jsonschema", JSONSchema); _ConfigExpression.register("z", zod.z); _ConfigExpression.register("assert", chai.assert); _ConfigExpression.register("expect", chai.expect); _ConfigExpression.register("should", chai.should); } static escapeRegex(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } static pattern = new RegExp( _ConfigExpression.delimiters.map(({ start, end }) => `${_ConfigExpression.escapeRegex(start)}(.*?)${_ConfigExpression.escapeRegex(end)}`).join("|"), "g" ); static register(key, registeree) { if (key.endsWith(_ConfigExpression.suffix)) { _ConfigExpression.globals.set(key.slice(0, -_ConfigExpression.suffix.length), registeree); } else { _ConfigExpression.globals.set(key, registeree); } } static parse(raw) { try { const expression = raw.replace(_ConfigExpression.pattern, (match, group) => { return `${_ConfigExpression.marker.start}${group}${_ConfigExpression.marker.end}`; }); const ast = acorn__namespace.parse(expression, { ecmaVersion: "latest" }); return { raw, expression, ast }; } catch (error) { throw new Error(`Failed to parse expression "${raw}" ${error}`); } } static evaluate(expression, context = {}) { try { const parsed = _ConfigExpression.parse(expression); const compartment = new Compartment({ ...context, ...Object.fromEntries(_ConfigExpression.globals) }); return compartment.evaluate(parsed.expression); } catch (error) { throw new Error(`Failed to evaluate expression "${expression}" ${error.message}`); } } static evaluateBoolean(expression, context) { return _ConfigExpression.evaluate(`Boolean(${expression})`, context); } static evaluateTemplateString(expression, context) { return _ConfigExpression.evaluate(`\`${expression}\``, context); } static references(expression) { const parsed = _ConfigExpression.parse(expression); const references = /* @__PURE__ */ new Set(); walk__namespace.simple(parsed.ast, { Identifier(node) { references.add(node.name); } }); return Array.from(references); } static sort(expressionsDict) { const graph = _9__default.default.chain(expressionsDict).mapValues((expression) => _ConfigExpression.references(expression)).value(); const sorted = []; const visited = /* @__PURE__ */ new Set(); const visiting = /* @__PURE__ */ new Set(); const visit = /* @__PURE__ */ __name((node, ancestors = []) => { if (visiting.has(node)) { throw new Error(`Cyclic dependency detected: ${ancestors.join(" -> ")} -> ${node}`); } if (!visited.has(node)) { visiting.add(node); (graph[node] || []).forEach((neighbor) => { visit(neighbor, [...ancestors, node]); }); visiting.delete(node); visited.add(node); sorted.push(node); } }, "visit"); _9__default.default.forEach(graph, (referances, key) => { if (!visited.has(key)) { visit(key); } }); return sorted.filter((key) => Object.prototype.hasOwnProperty.call(expressionsDict, key)); } }; // src/ConfigValue.ts var ConfigValue = class _ConfigValue { static { __name(this, "ConfigValue"); } static parse(value) { try { return JSON.parse(value); } catch (error) { return value; } } static stringify(value) { if (_9__default.default.isString(value)) { return value; } return JSON.stringify(value); } static createEvaluationContext(context) { const configs = _9__default.default.mapValues(context.configs, (current) => ({ ...current, storedValue: current.value, value: _ConfigValue.parse(current.value), labels: Array.isArray(current.cfgu?.label) ? current.cfgu.label : _9__default.default.compact([current.cfgu?.label]) })); let $ = { configs }; if (context.store && context.set && context.schema) { $ = { input: { store: { ...context.store }, set: { ...context.set }, schema: { ...context.schema } }, ...$ }; } if (context.current) { const currentConfig = configs[context.current]; if (!currentConfig) { throw new Error(`Failed to create evaluation context for key "${context.current}"`); } $ = { ...currentConfig, ...$ }; } return { $ }; } static test({ test, errorSuffix = `test "${test}"`, context }) { try { const isValid = ConfigExpression.evaluateBoolean(test, _ConfigValue.createEvaluationContext(context)); if (!isValid) { throw new Error(); } } catch (error) { throw new Error(`ConfigValue failed ${errorSuffix} ${error.message}`); } } static validate(context) { const currentConfig = context.configs[context.current]; if (!currentConfig) { throw new Error(`Failed to create evaluation context for key "${context.current}"`); } const { cfgu } = currentConfig; if (cfgu?.pattern) { _ConfigValue.test({ test: `JSONSchema.validate({ "type": "string", "pattern": $.cfgu.pattern }, $.storedValue) || true`, errorSuffix: "Cfgu.pattern test", context }); } if (cfgu?.enum) { _ConfigValue.test({ test: `JSONSchema.validate({ "enum": $.cfgu.enum }, $.value) || true`, errorSuffix: "Cfgu.enum test", context }); } if (cfgu?.schema) { _ConfigValue.test({ test: `JSONSchema.validate($.cfgu.schema, $.value) || true`, errorSuffix: "Cfgu.schema test", context }); } if (cfgu?.test) { const tests = Array.isArray(cfgu.test) ? cfgu.test : [cfgu.test]; tests.forEach((test, idx) => { _ConfigValue.test({ test, errorSuffix: `Cfgu.test[${idx}] "${test}"`, context }); }); } } }; // src/ConfigSchema.ts var LEGACY_CFGU_VALUE_TYPE_VALIDATORS = { Boolean: "validator.isBoolean($.storedValue, { loose: true })", Number: "validator.isNumeric($.storedValue)", String: "true", RegEx: "expect($.cfgu.pattern).to.exist", UUID: "validator.isUUID($.storedValue)", SemVer: "validator.isSemVer($.storedValue)", Email: "validator.isEmail($.storedValue)", MobilePhone: "validator.isMobilePhone($.storedValue)", Locale: "validator.isLocale($.storedValue)", LatLong: "validator.isLatLong($.storedValue)", Color: "validator.isHexColor($.storedValue) || validator.isHSL($.storedValue) || validator.isRgbColor($.storedValue)", IPv4: "validator.isIP($.storedValue, 4)", IPv6: "validator.isIP($.storedValue, 6)", Domain: "validator.isFQDN($.storedValue)", URL: "validator.isURL($.storedValue)", Hex: "validator.isHexadecimal($.storedValue)", Base64: "validator.isBase64($.storedValue)", MD5: 'validator.isHash($.storedValue, "md5")', SHA: 'validator.isHash($.storedValue, "sha1") || validator.isHash($.storedValue, "sha256") || validator.isHash($.storedValue, "sha384") || validator.isHash($.storedValue, "sha512")', Country: "validator.isISO31661Alpha2($.storedValue) || validator.isISO31661Alpha3($.storedValue)", Currency: "validator.isISO4217($.storedValue)", DockerImage: "/^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?.)+[a-z]{2,6}(?::d{1,5})?/)?[a-z0-9]+(?:[._-/:][a-z0-9]+)*$/gm.test($.storedValue)", ARN: "/^arn:([^:\n]+):([^:\n]+):(?:[^:\n]*):(?:([^:\n]*)):([^:/\n]+)(?:(:[^\n]+)|(/[^:\n]+))?$/gm.test($.storedValue)", MACAddress: "validator.isMACAddress($.storedValue)", MIMEType: "validator.isMimeType($.storedValue)", MongoId: "validator.isMongoId($.storedValue)", AWSRegion: 'new Set(["af-south-1","ap-east-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-south-1","ap-southeast-1","ap-southeast-2","ca-central-1","cn-north-1","cn-northwest-1","eu-central-1","eu-north-1","eu-south-1","eu-west-1","eu-west-2","eu-west-3","me-south-1","sa-east-1","us-east-1","us-east-2","us-gov-east-1","us-gov-west-1","us-west-1","us-west-2"]).has($.storedValue)', AZRegion: 'new Set(["eastus","eastus2","centralus","northcentralus","southcentralus","westcentralus","westus","westus2","canadacentral","canadaeast","brazilsouth","brazilsoutheast","northeurope","westeurope","uksouth","ukwest","francecentral","francesouth","switzerlandnorth","switzerlandwest","germanywestcentral","norwayeast","norwaywest","eastasia","southeastasia","australiaeast","australiasoutheast","australiacentral","australiacentral2","japaneast","japanwest","koreacentral","koreasouth","southafricanorth","southafricawest","uaenorth","uaecentral","usgovarizona","usgovtexas","usdodeast","usdodcentral","usgovvirginia","usgoviowa","usgovcalifornia","ussecwest","usseceast"]).has($.storedValue)', GCPRegion: 'new Set(["us-east1","us-east4","us-west1","us-west2","us-west3","us-central1","northamerica-northeast1","southamerica-east1","europe-north1","europe-west1","europe-west2","europe-west3","europe-west4","europe-west6","asia-east1","asia-east2","asia-northeast1","asia-northeast2","asia-northeast3","asia-south1","asia-southeast1","australia-southeast1","australia-southeast2","southasia-east1","northamerica-northeast2","europe-central2","asia-southeast2","asia-east3","europe-west7","us-west4","europe-west8","asia-northeast4","asia-southeast3","us-west5","us-central2","us-east5","us-north1","northamerica-northeast3","us-west6"]).has($.storedValue)', OracleRegion: 'new Set(["us-ashburn-1","us-phoenix-1","ca-toronto-1","sa-saopaulo-1","uk-london-1","uk-gov-london-1","eu-frankfurt-1","eu-zurich-1","eu-amsterdam-1","me-jeddah-1","ap-mumbai-1","ap-osaka-1","ap-seoul-1","ap-sydney-1","ap-tokyo-1","ap-chuncheon-1","ap-melbourne-1","ap-hyderabad-1","ca-montreal-1","us-sanjose-1","us-luke-1","me-dubai-1","us-gov-ashburn-1","us-gov-chicago-1","us-gov-phoenix-1","us-gov-orlando-1","us-gov-sanjose-1","us-gov-ashburn-2"]).has($.storedValue)', IBMRegion: 'new Set(["us-south","us-east","us-north","us-west","eu-gb","eu-de","eu-nl","eu-fr","eu-it","ap-north","ap-south","ap-east","ap-jp","ap-au","ca-toronto","ca-central","sa-saopaulo","sa-mexico","sa-buenosaires","sa-lima","sa-santiago","af-za","af-eg","af-dz","af-ma"]).has($.storedValue)', AlibabaRegion: 'new Set(["cn-hangzhou","cn-shanghai","cn-beijing","cn-shenzhen","cn-zhangjiakou","cn-huhehaote","cn-wulanchabu","ap-southeast-1","ap-southeast-2","ap-southeast-3","ap-southeast-5","ap-northeast-1","ap-south-1","ap-south-2","us-west-1","us-east-1","eu-west-1","eu-central-1","me-east-1","ap-southwest-1"]).has($.storedValue)', Language: "validator.isISO6391($.storedValue)", DateTime: "validator.isDate($.storedValue) || validator.isTime($.storedValue) || !Number.isNaN(new Date($.storedValue).getTime())", JSONSchema: "expect($.cfgu.schema).to.exist" }; var ConfigSchemaKeysSchema = { type: "object", required: [], minProperties: 1, // todo: patternProperties is not supported by OpenAPI and has limited error resolution support. Thats why we currently use additionalProperties and check key Naming separately. // additionalProperties: false, // patternProperties: { // [Naming.pattern]: CfguSchema, // }, additionalProperties: CfguSchema }; var ConfigSchema = class _ConfigSchema { constructor(keys = {}) { this.keys = keys; if (_9__default.default.isEmpty(this.keys)) { throw new Error("ConfigSchema.keys is required"); } _9__default.default.chain(this.keys).entries().forEach(([key, cfgu]) => { ConfigKey.validate({ key, errorPrefix: "ConfigSchema.keys" }); }); try { JSONSchema.validate(ConfigSchemaKeysSchema, this.keys); } catch (error) { throw new Error(`ConfigSchema.keys are invalid ${error.message}`); } } static { __name(this, "ConfigSchema"); } static fromLegacyConfigSchema(contents) { const migratedContents = _9__default.default(contents).mapValues((value, key) => { const { type, options, depends, labels, template, ...restCfguProps } = value; const migratedCfgu = { ...restCfguProps, test: [] }; if (type) { migratedCfgu.test.push(LEGACY_CFGU_VALUE_TYPE_VALIDATORS[type]); } if (options) { migratedCfgu.enum = options.map((option) => ConfigValue.parse(option)); } if (labels) { migratedCfgu.label = labels; } if (depends) { depends.forEach((dependencyKey) => { migratedCfgu.test.push( `expect($.configs.${dependencyKey}.storedValue, 'Dependency ${dependencyKey} is missing for ${key}').to.not.be.empty` ); }); } if (template) { migratedCfgu.const = template.replace(ConfigExpression.pattern, (match, group) => { if (contents[group]) { return `{{$.configs.${group}.storedValue}}`; } if (group === "CONFIGU_STORE.type") { return `{{$.input.store.type}}`; } if (group === "CONFIGU_SET.path") { return `{{$.input.set.path}}`; } if (group === "CONFIGU_SET.hierarchy") { return `{{$.input.set.hierarchy}}`; } if (group === "CONFIGU_SET.first") { return `{{_.first($.input.set.hierarchy)}}`; } if (group === "CONFIGU_SET.last") { return `{{_.last($.input.set.hierarchy)}}`; } return match; }); } return migratedCfgu; }).value(); return new _ConfigSchema(migratedContents); } }; var ConfigSet = class _ConfigSet { constructor(path = _ConfigSet.root) { this.path = path; this.path = this.path.trim(); if (this.path.startsWith(_ConfigSet.rootLabel)) { this.path = this.path.slice(1); } if (this.path.endsWith(_ConfigSet.separator)) { this.path = this.path.slice(0, -1); } if (this.path === _ConfigSet.root) { this.hierarchy = [_ConfigSet.root]; return; } this.hierarchy = this.path.split(_ConfigSet.separator).map((cur, idx, sets) => { ConfigKey.validate({ key: cur, errorPrefix: "ConfigSet.path" }); return _9__default.default.take(sets, idx + 1).join(_ConfigSet.separator); }); this.hierarchy.unshift(_ConfigSet.root); } static { __name(this, "ConfigSet"); } static separator = "/"; static root = ""; static rootLabel = "/"; hierarchy = []; }; // src/ConfigStore.ts var ConfigStore = class _ConfigStore { static { __name(this, "ConfigStore"); } static stores = /* @__PURE__ */ new Map(); // todo: verify if this is needed except for jsonify type = this.constructor.name; static get type() { if (!this.name.endsWith(_ConfigStore.name)) { throw new Error(`ConfigStore class name must end with "${_ConfigStore.name}". (${this.name})`); } const nameWithoutSuffix = this.name.slice(0, -_ConfigStore.name.length); const type = ConfigKey.normalize(nameWithoutSuffix); return type; } static register(store) { _ConfigStore.stores.set(store.type, store); } static has(type) { const normalizedType = ConfigKey.normalize(type); return _ConfigStore.stores.has(normalizedType); } static construct(type, configuration = {}) { const normalizedType = ConfigKey.normalize(type); const StoreCtor = _ConfigStore.stores.get(normalizedType); if (!StoreCtor) { throw new Error(`unknown store type ${type}`); } return new StoreCtor(configuration); } async init() { } }; // src/stores/Noop.ts var NoopConfigStore = class extends ConfigStore { static { __name(this, "NoopConfigStore"); } get(queries) { return Promise.resolve([]); } set(configs) { return Promise.resolve(); } }; var InMemoryConfigStore = class extends ConfigStore { static { __name(this, "InMemoryConfigStore"); } data = {}; async get(queries) { return _9__default.default.chain(queries).map(({ set, key }) => _9__default.default.get(this.data, [set, key], { set, key, value: "" })).filter("value").value(); } async set(configs) { configs.forEach((config) => { if (!config.value) { _9__default.default.unset(this.data, [config.set, config.key]); } _9__default.default.set(this.data, [config.set, config.key], config); }); } }; // package.json var package_default = { name: "@configu/sdk", version: "1.1.0", description: "Configu TypeScript SDK", keywords: [ "configu-sdk", "configu-ts", "configu-typescript", "sdk", "typescript", "typescript-sdk" ], homepage: "https://configu.com", repository: { type: "git", url: "git+https://github.com/configu/configu", directory: "packages/sdk" }, bugs: "https://github.com/configu/configu/issues", license: "Apache-2.0", author: { name: "@configu/dev", email: "dev@configu.com", url: "https://configu.com" }, type: "module", types: "./dist/index.d.ts", main: "./dist/index.cjs", module: "./dist/index.mjs", exports: { "./package.json": "./package.json", ".": { import: { types: "./dist/index.d.ts", default: "./dist/index.mjs" }, require: { types: "./dist/index.d.cts", default: "./dist/index.cjs" } } }, files: [ "dist" ], config: { entry: "./src/index.ts" }, scripts: { clean: "shx rm -rf dist", build: "tsup $npm_package_config_entry" }, dependencies: { "@segment/ajv-human-errors": "^2.14.1", "@vitest/expect": "^2.1.8", acorn: "^8.14.0", "acorn-walk": "^8.3.4", ajv: "^8.17.1", "ajv-formats": "^3.0.1", chai: "^5.1.2", "chai-subset": "^1.6.0", "jest-extended": "^4.0.2", "json-schema-to-ts": "^3.1.1", lodash: "^4.17.21", ses: "^1.10.0", validator: "^13.12.0", zod: "^3.24.1" }, devDependencies: { "@types/chai": "^5.0.1", "@types/chai-subset": "^1.3.5", "@types/validator": "^13.12.2", "type-fest": "^4.30.2" } }; // src/ConfigCommand.ts var ConfigCommand = class { constructor(input) { this.input = input; } static { __name(this, "ConfigCommand"); } async run() { const start = performance.now(); const result = await this.execute(); const end = performance.now(); const duration = end - start; return { metadata: { version: package_default.version, start, end, duration }, result }; } }; var EvaluatedConfigOrigin = /* @__PURE__ */ ((EvaluatedConfigOrigin2) => { EvaluatedConfigOrigin2["Const"] = "const"; EvaluatedConfigOrigin2["Override"] = "override"; EvaluatedConfigOrigin2["Store"] = "store"; EvaluatedConfigOrigin2["Default"] = "default"; EvaluatedConfigOrigin2["Empty"] = "empty"; return EvaluatedConfigOrigin2; })(EvaluatedConfigOrigin || {}); var EvalCommand = class extends ConfigCommand { static { __name(this, "EvalCommand"); } async execute() { const { store } = this.input; await store.init(); let result = { ...this.evalEmpty() }; result = { ...result, ...this.evalOverride(result) }; result = { ...result, ...await this.evalStore(result) }; result = { ...result, ...this.evalDefault(result) }; result = { ...result, ...this.evalPipe(result) }; result = { ...result, ...this.evalConst(result) }; this.validateResult(result); return result; } evalEmpty() { const { schema } = this.input; return _9__default.default.mapValues(schema.keys, (cfgu, key) => { let origin = "empty" /* Empty */; if (cfgu?.const) { origin = "const" /* Const */; } return { set: this.input.set.path, key, value: "", cfgu, origin }; }); } evalOverride(result) { const { configs = {} } = this.input; return _9__default.default.mapValues(result, (current) => { if (current.origin !== "empty" /* Empty */) { return current; } const isOverridden = Object.prototype.hasOwnProperty.call(configs, current.key); const isLazy = Boolean(current.cfgu?.lazy); if (!isOverridden && !isLazy) { return current; } const overrideValue = configs?.[current.key] ?? ""; return { ...current, value: ConfigValue.stringify(overrideValue), origin: "override" /* Override */ }; }); } async evalStore(result) { const { store, set } = this.input; const storeQueries = _9__default.default.chain(result).values().filter((current) => current.origin === "empty" /* Empty */).flatMap((current) => set.hierarchy.map((node) => ({ set: node, key: current.key }))).value(); const storeConfigsArray = await store.get(storeQueries); const storeConfigsDict = _9__default.default.chain(storeConfigsArray).orderBy([(config) => set.hierarchy.indexOf(config.set)], ["asc"]).keyBy((config) => config.key).value(); return _9__default.default.mapValues(result, (current) => { if (current.origin !== "empty" /* Empty */) { return current; } const storeConfig = storeConfigsDict?.[current.key]; if (!storeConfig || !storeConfig.value) { return current; } return _9__default.default.merge(current, storeConfig, { origin: "store" /* Store */ }); }); } evalDefault(result) { return _9__default.default.mapValues(result, (current) => { if (current.origin !== "empty" /* Empty */) { return current; } if (current.cfgu?.default) { return { ...current, value: ConfigValue.stringify(current.cfgu.default), origin: "default" /* Default */ }; } return current; }); } evalPipe(result) { const { pipe } = this.input; if (!pipe) { return result; } const mergedResults = _9__default.default.assignWith(result, pipe, (current, piped) => { if (piped.origin === "empty" /* Empty */) { return current; } if (current.origin === "empty" /* Empty */) { return piped; } const isCurrentDefault = current.origin === "default" /* Default */; const isPipedDefault = piped.origin === "default" /* Default */; if (isCurrentDefault && !isPipedDefault) { return piped; } return current; }); return mergedResults; } evalConst(result) { const { store, set, schema } = this.input; const resultWithConstExpressions = { ...result }; const constExpressionsDict = _9__default.default.chain(result).pickBy((current) => current.origin === "const" /* Const */).mapValues((current) => current.cfgu?.const ? `\`${current.cfgu.const}\`` : "").value(); ConfigExpression.sort(constExpressionsDict).forEach((key) => { const expression = constExpressionsDict[key]; const value = ConfigExpression.evaluate( expression, ConfigValue.createEvaluationContext({ store, set, schema, current: key, configs: resultWithConstExpressions }) ) ?? ""; resultWithConstExpressions[key].value = value; }); return resultWithConstExpressions; } validateResult(result) { const { store, set, schema, validate = true } = this.input; if (!validate) { return; } _9__default.default.chain(result).values().forEach((current) => { const { cfgu, origin, key } = current; try { if (cfgu?.required && origin === "empty" /* Empty */) { throw new Error("ConfigValue is required"); } ConfigValue.validate({ store, set, schema, current: key, configs: result }); } catch (error) { throw new Error(`Validation failed for Config: "${current.key}" ${error.message}`); } }).value(); } }; var ConfigDiffAction = /* @__PURE__ */ ((ConfigDiffAction2) => { ConfigDiffAction2["Add"] = "add"; ConfigDiffAction2["Update"] = "update"; ConfigDiffAction2["Delete"] = "delete"; return ConfigDiffAction2; })(ConfigDiffAction || {}); var UpsertCommand = class extends ConfigCommand { static { __name(this, "UpsertCommand"); } async execute() { const { store, set, schema, configs = {}, pipe = {} } = this.input; await store.init(); const keys = Object.keys(schema.keys); const storeQueries = _9__default.default.chain(keys).map((key) => ({ set: set.path, key })).value(); const storeConfigsArray = await store.get(storeQueries); const storeConfigsDict = _9__default.default.chain(storeConfigsArray).keyBy((config) => config.key).mapValues((config) => config.value).value(); const currentConfigs = _9__default.default.chain(keys).keyBy().mapValues((key) => ({ set: set.path, key, value: storeConfigsDict[key] ?? "", cfgu: schema.keys[key] })).value(); let result = {}; if (_9__default.default.isEmpty(configs) && _9__default.default.isEmpty(pipe)) { result = _9__default.default.chain(schema.keys).mapValues((cfgu, key) => ({ set: set.path, key, value: "", cfgu, prev: currentConfigs[key]?.value ?? "", next: "", action: "delete" /* Delete */ })).pickBy((diff) => diff.prev !== diff.next).value(); } else { const kvConfigs = _9__default.default.mapValues(configs, (value) => ConfigValue.stringify(value)); const pipeConfigs = _9__default.default.chain(pipe).pickBy((value, key) => { const cfgu = schema.keys[key]; return cfgu && // key exists in current schema !cfgu.const && // key is not a const in current schema !cfgu.lazy && // key is not lazy in current schema value.origin === "store" /* Store */; }).mapValues((value) => value.value).value(); const upsertConfigsDict = { ...pipeConfigs, ...kvConfigs }; result = _9__default.default.chain(upsertConfigsDict).mapValues((value, key) => { try { const cfgu = schema.keys[key]; if (!cfgu) { throw new Error(`Key is not declared on schema`); } const prev = currentConfigs[key]?.value ?? ""; const next = value; const context = { set: set.path, key, value, cfgu, prev, next, action: "update" /* Update */ }; if (prev === next) { return context; } if (next === "") { return { ...context, action: "delete" /* Delete */ }; } if (prev) { return { ...context, action: "update" /* Update */ }; } return { ...context, action: "add" /* Add */ }; } catch (error) { throw new Error(`Validation failed for Config: "${key}" ${error.message}`); } }).pickBy((diff) => diff.prev !== diff.next).value(); } _9__default.default.chain(result).entries().forEach(([key, current]) => { try { if (current.value) { if (current.cfgu?.lazy) { throw new Error(`Key declared as "lazy" cannot be assigned a value`); } if (current.cfgu?.const) { throw new Error(`Key declared as "const" cannot be assigned a value`); } ConfigValue.validate({ store, set, schema, current: key, configs: result }); } } catch (error) { throw new Error(`Validation failed for Config: "${key}" ${error.message}`); } }).value(); if (!this.input.dry) { const upsertConfigsArray = _9__default.default.chain(result).entries().map(([key, diff]) => ({ set: set.path, key, value: diff.next })).value(); await store.set(upsertConfigsArray); } return result; } }; var ExportCommand = class extends ConfigCommand { static { __name(this, "ExportCommand"); } async execute() { const { pipe } = this.input; const filteredPipe = _9__default.default.pickBy(pipe, (config) => !config.cfgu?.hidden); this.validatePipe(filteredPipe); const mappedPipe = this.kv(filteredPipe); return mappedPipe; } validatePipe(pipe) { _9__default.default.chain(pipe).values().forEach((current) => { ConfigKey.validate({ key: current.key }); }); } kv(pipe) { const { coerce = true } = this.input; if (!coerce) { return _9__default.default.chain(pipe).keyBy("key").mapValues("value").value(); } return _9__default.default.chain(pipe).keyBy("key").mapValues(({ value }) => ConfigValue.parse(value)).value(); } }; Object.defineProperty(exports, "_", { enumerable: true, get: function () { return _9__default.default; } }); Object.defineProperty(exports, "validator", { enumerable: true, get: function () { return validator__default.default; } }); Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod.z; } }); Object.defineProperty(exports, "assert", { enumerable: true, get: function () { return chai.assert; } }); Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return chai.expect; } }); Object.defineProperty(exports, "should", { enumerable: true, get: function () { return chai.should; } }); exports.CfguSchema = CfguSchema; exports.ConfigCommand = ConfigCommand; exports.ConfigDiffAction = ConfigDiffAction; exports.ConfigExpression = ConfigExpression; exports.ConfigKey = ConfigKey; exports.ConfigSchema = ConfigSchema; exports.ConfigSchemaKeysSchema = ConfigSchemaKeysSchema; exports.ConfigSet = ConfigSet; exports.ConfigStore = ConfigStore; exports.ConfigValue = ConfigValue; exports.EvalCommand = EvalCommand; exports.EvaluatedConfigOrigin = EvaluatedConfigOrigin; exports.ExportCommand = ExportCommand; exports.InMemoryConfigStore = InMemoryConfigStore; exports.JSONSchema = JSONSchema; exports.NoopConfigStore = NoopConfigStore; exports.UpsertCommand = UpsertCommand; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map