New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@app-config/extensions

Package Overview
Dependencies
Maintainers
2
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@app-config/extensions - npm Package Compare versions

Comparing version

to
2.5.0

dist/env-directive.d.ts

35

dist/es/index.d.ts
import { ParsingExtension } from '@app-config/core';
import { EnvironmentAliases } from '@app-config/node';
export { tryDirective } from './try-directive';
export { ifDirective } from './if-directive';
export { eqDirective } from './eq-directive';
export { hiddenDirective } from './hidden-directive';
export { envDirective } from './env-directive';
export { extendsDirective, extendsSelfDirective, overrideDirective } from './extends-directive';
export { timestampDirective } from './timestamp-directive';
export { envVarDirective } from './env-var-directive';
export { substituteDirective } from './substitute-directive';
export { substituteDirective as environmentVariableSubstitution } from './substitute-directive';
export { parseDirective } from './parse-directive';
/** Marks all values recursively as fromSecrets, so they do not trigger schema errors */

@@ -7,24 +17,1 @@ export declare function markAllValuesAsSecret(): ParsingExtension;

export declare function unescape$Directives(): ParsingExtension;
/** Try an operation, with a fallback ($try, $value and $fallback) */
export declare function tryDirective(): ParsingExtension;
/** Checks a condition, uses then/else */
export declare function ifDirective(): ParsingExtension;
/** Checks if two values are equal */
export declare function eqDirective(): ParsingExtension;
/** Prpoerties that are removed, used by references */
export declare function hiddenDirective(): ParsingExtension;
/** Uses another file as overriding values, layering them on top of current file */
export declare function overrideDirective(): ParsingExtension;
/** Uses another file as a "base", and extends on top of it */
export declare function extendsDirective(): ParsingExtension;
/** Lookup a property in the same file, and "copy" it */
export declare function extendsSelfDirective(): ParsingExtension;
/** Looks up an environment-specific value ($env) */
export declare function envDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
/** Provides the current timestamp using { $timestamp: true } */
export declare function timestampDirective(dateSource?: () => Date): ParsingExtension;
/** Substitues environment variables */
export declare function envVarDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
/** Substitues environment variables found in strings (similar to bash variable substitution) */
export declare function substituteDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
export declare const environmentVariableSubstitution: typeof substituteDirective;

@@ -1,13 +0,20 @@

import isEqual from 'lodash.isequal';
import { forKey, keysToPath, validateOptions, validationFunction, } from '@app-config/extension-utils';
import { ParsedValue, AppConfigError, NotFoundError, FailedToSelectSubObject, Fallbackable, InObject, } from '@app-config/core';
import { currentEnvironment, defaultAliases, resolveFilepath, FileSource, } from '@app-config/node';
import { logger } from '@app-config/logging';
import { named } from '@app-config/extension-utils';
export { tryDirective } from './try-directive';
export { ifDirective } from './if-directive';
export { eqDirective } from './eq-directive';
export { hiddenDirective } from './hidden-directive';
export { envDirective } from './env-directive';
export { extendsDirective, extendsSelfDirective, overrideDirective } from './extends-directive';
export { timestampDirective } from './timestamp-directive';
export { envVarDirective } from './env-var-directive';
export { substituteDirective } from './substitute-directive';
export { substituteDirective as environmentVariableSubstitution } from './substitute-directive';
export { parseDirective } from './parse-directive';
/** Marks all values recursively as fromSecrets, so they do not trigger schema errors */
export function markAllValuesAsSecret() {
return (value) => (parse) => parse(value, { fromSecrets: true });
return named('markAllValuesAsSecret', (value) => (parse) => parse(value, { fromSecrets: true }));
}
/** When a key $$foo is seen, change it to be $foo and mark with meta property fromEscapedDirective */
export function unescape$Directives() {
return (value, [_, key]) => {
return named('unescape$', (value, [_, key]) => {
if (typeof key === 'string' && key.startsWith('$$')) {

@@ -19,363 +26,4 @@ return async (parse) => {

return false;
};
}
/** Try an operation, with a fallback ($try, $value and $fallback) */
export function tryDirective() {
return forKey('$try', validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema()
.addProperty('$value', SchemaBuilder.fromJsonSchema({}))
.addProperty('$fallback', SchemaBuilder.fromJsonSchema({}))
.addBoolean('$unsafe', {}, false), (value) => async (parse) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { $value, $fallback, $unsafe } = value;
try {
return await parse($value, { shouldFlatten: true });
}
catch (error) {
if (error instanceof Fallbackable || $unsafe) {
return parse($fallback, { shouldFlatten: true });
}
throw error;
}
}, { lazy: true }));
}
/** Checks a condition, uses then/else */
export function ifDirective() {
return forKey('$if', validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema()
.addProperty('$check', SchemaBuilder.fromJsonSchema({}))
.addProperty('$then', SchemaBuilder.fromJsonSchema({}))
.addProperty('$else', SchemaBuilder.fromJsonSchema({})), (value) => async (parse) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { $check, $then, $else } = value;
const condition = (await parse($check)).toJSON();
if (condition) {
return parse($then, { shouldFlatten: true });
}
return parse($else, { shouldFlatten: true });
}, { lazy: true }));
}
/** Checks if two values are equal */
export function eqDirective() {
return forKey('$eq', validateOptions((SchemaBuilder) => SchemaBuilder.arraySchema(SchemaBuilder.fromJsonSchema({})), (values) => async (parse) => {
for (const a of values) {
for (const b of values) {
if (a === b)
continue;
if (isEqual(a, b))
continue;
return parse(false, { shouldFlatten: true });
}
}
return parse(true, { shouldFlatten: true });
}));
}
/** Prpoerties that are removed, used by references */
export function hiddenDirective() {
return forKey('$hidden', () => async (parse) => {
return parse({}, { shouldMerge: true });
});
}
/** Uses another file as overriding values, layering them on top of current file */
export function overrideDirective() {
return fileReferenceDirective('$override', { shouldOverride: true });
}
/** Uses another file as a "base", and extends on top of it */
export function extendsDirective() {
return fileReferenceDirective('$extends', { shouldMerge: true });
}
/** Lookup a property in the same file, and "copy" it */
export function extendsSelfDirective() {
const validate = validationFunction(({ stringSchema }) => stringSchema());
return forKey('$extendsSelf', (input, key, ctx) => async (parse, _, __, ___, root) => {
const value = (await parse(input)).toJSON();
validate(value, [...ctx, key]);
// we temporarily use a ParsedValue literal so that we get the same property lookup semantics
const selected = ParsedValue.literal(root).property(value.split('.'));
if (selected === undefined) {
throw new AppConfigError(`$extendsSelf selector was not found (${value})`);
}
if (selected.asObject() !== undefined) {
return parse(selected.toJSON(), { shouldMerge: true });
}
return parse(selected.toJSON(), { shouldFlatten: true });
});
}
/** Looks up an environment-specific value ($env) */
export function envDirective(aliases = defaultAliases, environmentOverride, environmentSourceNames) {
const environment = environmentOverride ?? currentEnvironment(aliases, environmentSourceNames);
const metadata = { shouldOverride: true };
return forKey('$env', validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema().addAdditionalProperties(), (value, _, ctx) => (parse) => {
if (!environment) {
if ('none' in value) {
return parse(value.none, metadata);
}
if ('default' in value) {
return parse(value.default, metadata);
}
throw new AppConfigError(`An $env directive was used (in ${keysToPath(ctx)}), but current environment (eg. NODE_ENV) is undefined`);
}
for (const [envName, envValue] of Object.entries(value)) {
if (envName === environment || aliases[envName] === environment) {
return parse(envValue, metadata);
}
}
if ('default' in value) {
return parse(value.default, metadata);
}
const found = Object.keys(value).join(', ');
throw new AppConfigError(`An $env directive was used (in ${keysToPath(ctx)}), but none matched the current environment (wanted ${environment}, saw [${found}])`);
},
// $env is lazy so that non-applicable envs don't get evaluated
{ lazy: true }));
}
/** Provides the current timestamp using { $timestamp: true } */
export function timestampDirective(dateSource = () => new Date()) {
return forKey('$timestamp', validateOptions((SchemaBuilder) => SchemaBuilder.oneOf(SchemaBuilder.booleanSchema(), SchemaBuilder.emptySchema()
.addString('day', {}, false)
.addString('month', {}, false)
.addString('year', {}, false)
.addString('weekday', {}, false)
.addString('locale', {}, false)
.addString('timeZone', {}, false)
.addString('timeZoneName', {}, false)), (value) => (parse) => {
let formatted;
const date = dateSource();
if (value === true) {
formatted = date.toISOString();
}
else if (typeof value === 'object') {
const { locale, ...options } = value;
formatted = date.toLocaleDateString(locale, options);
}
else {
throw new AppConfigError('$timestamp was provided an invalid option');
}
return parse(formatted, { shouldFlatten: true });
}));
}
/** Substitues environment variables */
export function envVarDirective(aliases = defaultAliases, environmentOverride, environmentSourceNames) {
const envType = environmentOverride ?? currentEnvironment(aliases, environmentSourceNames);
return forKey('$envVar', (value, key, ctx) => async (parse) => {
let name;
let parseInt = false;
let parseFloat = false;
let parseBool = false;
if (typeof value === 'string') {
name = value;
}
else {
validateObject(value, [...ctx, key]);
if (Array.isArray(value))
throw new AppConfigError('$envVar was given an array');
const resolved = (await parse(value.name)).toJSON();
validateString(resolved, [...ctx, key, [InObject, 'name']]);
parseInt = !!(await parse(value.parseInt)).toJSON();
parseFloat = !!(await parse(value.parseFloat)).toJSON();
parseBool = !!(await parse(value.parseBool)).toJSON();
name = resolved;
}
const parseValue = (strValue) => {
if (parseInt) {
const parsed = Number.parseInt(strValue, 10);
if (Number.isNaN(parsed)) {
throw new AppConfigError(`Failed to parseInt(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
if (parseFloat) {
const parsed = Number.parseFloat(strValue);
if (Number.isNaN(parsed)) {
throw new AppConfigError(`Failed to parseFloat(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
if (parseBool) {
const parsed = strValue.toLowerCase() !== 'false' && strValue !== '0';
return parse(parsed, { shouldFlatten: true });
}
return parse(strValue, { shouldFlatten: true });
};
let resolvedValue = process.env[name];
if (!resolvedValue && name === 'APP_CONFIG_ENV') {
resolvedValue = envType;
}
if (resolvedValue) {
return parseValue(resolvedValue);
}
if (typeof value === 'object' && value.fallback !== undefined) {
const fallback = (await parse(value.fallback)).toJSON();
const allowNull = (await parse(value.allowNull)).toJSON();
if (allowNull) {
validateStringOrNull(fallback, [...ctx, key, [InObject, 'fallback']]);
}
else {
validateString(fallback, [...ctx, key, [InObject, 'fallback']]);
}
return parseValue(fallback);
}
throw new AppConfigError(`$envVar could not find ${name} environment variable`);
});
}
/** Substitues environment variables found in strings (similar to bash variable substitution) */
export function substituteDirective(aliases = defaultAliases, environmentOverride, environmentSourceNames) {
const envType = environmentOverride ?? currentEnvironment(aliases, environmentSourceNames);
return forKey(['$substitute', '$subs'], (value, key, ctx) => async (parse) => {
if (typeof value === 'string') {
return parse(performAllSubstitutions(value, envType), { shouldFlatten: true });
}
validateObject(value, [...ctx, key]);
if (Array.isArray(value))
throw new AppConfigError('$substitute was given an array');
const name = (await parse(selectDefined(value.name, value.$name))).toJSON();
validateString(name, [...ctx, key, [InObject, 'name']]);
const parseValue = async (strValue) => {
const parseInt = (await parse(selectDefined(value.parseInt, value.$parseInt))).toJSON();
if (parseInt) {
const parsed = Number.parseInt(strValue, 10);
if (Number.isNaN(parsed)) {
throw new AppConfigError(`Failed to parseInt(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
const parseFloat = (await parse(selectDefined(value.parseFloat, value.$parseFloat))).toJSON();
if (parseFloat) {
const parsed = Number.parseFloat(strValue);
if (Number.isNaN(parsed)) {
throw new AppConfigError(`Failed to parseFloat(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
const parseBool = (await parse(selectDefined(value.parseBool, value.$parseBool))).toJSON();
if (parseBool) {
const parsed = strValue.toLowerCase() !== 'false' && strValue !== '0';
return parse(parsed, { shouldFlatten: true });
}
return parse(strValue, { shouldFlatten: true });
};
let resolvedValue = process.env[name];
if (!resolvedValue && name === 'APP_CONFIG_ENV') {
resolvedValue = envType;
}
if (resolvedValue) {
return parseValue(resolvedValue);
}
if (value.fallback !== undefined || value.$fallback !== undefined) {
const fallback = (await parse(selectDefined(value.fallback, value.$fallback))).toJSON();
const allowNull = (await parse(selectDefined(value.allowNull, value.$allowNull))).toJSON();
if (allowNull) {
validateStringOrNull(fallback, [...ctx, key, [InObject, 'fallback']]);
}
else {
validateString(fallback, [...ctx, key, [InObject, 'fallback']]);
}
return parseValue(fallback);
}
throw new AppConfigError(`$substitute could not find ${name} environment variable`);
});
}
export const environmentVariableSubstitution = substituteDirective;
// common logic for $extends and $override
function fileReferenceDirective(keyName, meta) {
return forKey(keyName, validateOptions((SchemaBuilder) => {
const reference = SchemaBuilder.oneOf(SchemaBuilder.stringSchema(), SchemaBuilder.emptySchema()
.addString('path')
.addBoolean('optional', {}, false)
.addString('select', {}, false));
return SchemaBuilder.oneOf(reference, SchemaBuilder.arraySchema(reference));
}, (value) => async (_, __, context, extensions) => {
const retrieveFile = async (filepath, subselector, isOptional = false) => {
const resolvedPath = resolveFilepath(context, filepath);
logger.verbose(`Loading file for ${keyName}: ${resolvedPath}`);
const source = new FileSource(resolvedPath);
const parsed = await source.read(extensions).catch((error) => {
if (error instanceof NotFoundError && isOptional) {
return ParsedValue.literal({});
}
throw error;
});
if (subselector) {
const found = parsed.property(subselector.split('.'));
if (!found) {
throw new FailedToSelectSubObject(`Failed to select ${subselector} in ${resolvedPath}`);
}
return found;
}
return parsed;
};
let parsed;
if (typeof value === 'string') {
parsed = await retrieveFile(value);
}
else if (Array.isArray(value)) {
parsed = ParsedValue.literal({});
for (const ext of value) {
if (typeof ext === 'string') {
parsed = ParsedValue.merge(parsed, await retrieveFile(ext));
}
else {
const { path, optional, select } = ext;
parsed = ParsedValue.merge(parsed, await retrieveFile(path, select, optional));
}
}
}
else {
const { path, optional, select } = value;
parsed = await retrieveFile(path, select, optional);
}
return parsed.assignMeta(meta);
}));
}
function performAllSubstitutions(text, envType) {
let output = text;
/* eslint-disable-next-line no-constant-condition */
while (true) {
// this regex matches:
// $FOO
// ${FOO}
// ${FOO:-fallback}
// ${FOO:-${FALLBACK}}
//
// var name is group 1 || 2
// fallback value is group 3
// https://regex101.com/r/6ZMmx7/3
const match = /\$(?:([a-zA-Z_]\w+)|(?:{([a-zA-Z_]\w+)(?::- *(.*?) *)?}))/g.exec(output);
if (!match)
break;
const fullMatch = match[0];
const varName = match[1] || match[2];
const fallback = match[3];
if (varName) {
const env = process.env[varName];
if (env !== undefined) {
output = output.replace(fullMatch, env);
}
else if (fallback !== undefined) {
// we'll recurse again, so that ${FOO:-${FALLBACK}} -> ${FALLBACK} -> value
output = performAllSubstitutions(output.replace(fullMatch, fallback), envType);
}
else if (varName === 'APP_CONFIG_ENV') {
if (!envType) {
throw new AppConfigError(`Could not find environment variable ${varName}`);
}
// there's a special case for APP_CONFIG_ENV, which is always the envType
output = output.replace(fullMatch, envType);
}
else {
throw new AppConfigError(`Could not find environment variable ${varName}`);
}
}
}
logger.verbose(`Performed $substitute for "${text}" -> "${output}"`);
return output;
}
function selectDefined(...args) {
for (const a of args) {
if (a !== undefined)
return a;
}
return undefined;
}
const validateObject = validationFunction(({ emptySchema }) => emptySchema().addAdditionalProperties());
const validateString = validationFunction(({ stringSchema }) => stringSchema());
const validateStringOrNull = validationFunction(({ fromJsonSchema }) => fromJsonSchema({ type: ['null', 'string'] }));
//# sourceMappingURL=index.js.map
import { ParsingExtension } from '@app-config/core';
import { EnvironmentAliases } from '@app-config/node';
export { tryDirective } from './try-directive';
export { ifDirective } from './if-directive';
export { eqDirective } from './eq-directive';
export { hiddenDirective } from './hidden-directive';
export { envDirective } from './env-directive';
export { extendsDirective, extendsSelfDirective, overrideDirective } from './extends-directive';
export { timestampDirective } from './timestamp-directive';
export { envVarDirective } from './env-var-directive';
export { substituteDirective } from './substitute-directive';
export { substituteDirective as environmentVariableSubstitution } from './substitute-directive';
export { parseDirective } from './parse-directive';
/** Marks all values recursively as fromSecrets, so they do not trigger schema errors */

@@ -7,24 +17,1 @@ export declare function markAllValuesAsSecret(): ParsingExtension;

export declare function unescape$Directives(): ParsingExtension;
/** Try an operation, with a fallback ($try, $value and $fallback) */
export declare function tryDirective(): ParsingExtension;
/** Checks a condition, uses then/else */
export declare function ifDirective(): ParsingExtension;
/** Checks if two values are equal */
export declare function eqDirective(): ParsingExtension;
/** Prpoerties that are removed, used by references */
export declare function hiddenDirective(): ParsingExtension;
/** Uses another file as overriding values, layering them on top of current file */
export declare function overrideDirective(): ParsingExtension;
/** Uses another file as a "base", and extends on top of it */
export declare function extendsDirective(): ParsingExtension;
/** Lookup a property in the same file, and "copy" it */
export declare function extendsSelfDirective(): ParsingExtension;
/** Looks up an environment-specific value ($env) */
export declare function envDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
/** Provides the current timestamp using { $timestamp: true } */
export declare function timestampDirective(dateSource?: () => Date): ParsingExtension;
/** Substitues environment variables */
export declare function envVarDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
/** Substitues environment variables found in strings (similar to bash variable substitution) */
export declare function substituteDirective(aliases?: EnvironmentAliases, environmentOverride?: string, environmentSourceNames?: string[] | string): ParsingExtension;
export declare const environmentVariableSubstitution: typeof substituteDirective;
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.environmentVariableSubstitution = exports.substituteDirective = exports.envVarDirective = exports.timestampDirective = exports.envDirective = exports.extendsSelfDirective = exports.extendsDirective = exports.overrideDirective = exports.hiddenDirective = exports.eqDirective = exports.ifDirective = exports.tryDirective = exports.unescape$Directives = exports.markAllValuesAsSecret = void 0;
const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
exports.unescape$Directives = exports.markAllValuesAsSecret = exports.parseDirective = exports.environmentVariableSubstitution = exports.substituteDirective = exports.envVarDirective = exports.timestampDirective = exports.overrideDirective = exports.extendsSelfDirective = exports.extendsDirective = exports.envDirective = exports.hiddenDirective = exports.eqDirective = exports.ifDirective = exports.tryDirective = void 0;
const extension_utils_1 = require("@app-config/extension-utils");
const core_1 = require("@app-config/core");
const node_1 = require("@app-config/node");
const logging_1 = require("@app-config/logging");
var try_directive_1 = require("./try-directive");
Object.defineProperty(exports, "tryDirective", { enumerable: true, get: function () { return try_directive_1.tryDirective; } });
var if_directive_1 = require("./if-directive");
Object.defineProperty(exports, "ifDirective", { enumerable: true, get: function () { return if_directive_1.ifDirective; } });
var eq_directive_1 = require("./eq-directive");
Object.defineProperty(exports, "eqDirective", { enumerable: true, get: function () { return eq_directive_1.eqDirective; } });
var hidden_directive_1 = require("./hidden-directive");
Object.defineProperty(exports, "hiddenDirective", { enumerable: true, get: function () { return hidden_directive_1.hiddenDirective; } });
var env_directive_1 = require("./env-directive");
Object.defineProperty(exports, "envDirective", { enumerable: true, get: function () { return env_directive_1.envDirective; } });
var extends_directive_1 = require("./extends-directive");
Object.defineProperty(exports, "extendsDirective", { enumerable: true, get: function () { return extends_directive_1.extendsDirective; } });
Object.defineProperty(exports, "extendsSelfDirective", { enumerable: true, get: function () { return extends_directive_1.extendsSelfDirective; } });
Object.defineProperty(exports, "overrideDirective", { enumerable: true, get: function () { return extends_directive_1.overrideDirective; } });
var timestamp_directive_1 = require("./timestamp-directive");
Object.defineProperty(exports, "timestampDirective", { enumerable: true, get: function () { return timestamp_directive_1.timestampDirective; } });
var env_var_directive_1 = require("./env-var-directive");
Object.defineProperty(exports, "envVarDirective", { enumerable: true, get: function () { return env_var_directive_1.envVarDirective; } });
var substitute_directive_1 = require("./substitute-directive");
Object.defineProperty(exports, "substituteDirective", { enumerable: true, get: function () { return substitute_directive_1.substituteDirective; } });
var substitute_directive_2 = require("./substitute-directive");
Object.defineProperty(exports, "environmentVariableSubstitution", { enumerable: true, get: function () { return substitute_directive_2.substituteDirective; } });
var parse_directive_1 = require("./parse-directive");
Object.defineProperty(exports, "parseDirective", { enumerable: true, get: function () { return parse_directive_1.parseDirective; } });
/** Marks all values recursively as fromSecrets, so they do not trigger schema errors */
function markAllValuesAsSecret() {
return (value) => (parse) => parse(value, { fromSecrets: true });
return extension_utils_1.named('markAllValuesAsSecret', (value) => (parse) => parse(value, { fromSecrets: true }));
}

@@ -30,3 +36,3 @@ exports.markAllValuesAsSecret = markAllValuesAsSecret;

function unescape$Directives() {
return (value, [_, key]) => {
return extension_utils_1.named('unescape$', (value, [_, key]) => {
if (typeof key === 'string' && key.startsWith('$$')) {

@@ -38,375 +44,5 @@ return async (parse) => {

return false;
};
});
}
exports.unescape$Directives = unescape$Directives;
/** Try an operation, with a fallback ($try, $value and $fallback) */
function tryDirective() {
return extension_utils_1.forKey('$try', extension_utils_1.validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema()
.addProperty('$value', SchemaBuilder.fromJsonSchema({}))
.addProperty('$fallback', SchemaBuilder.fromJsonSchema({}))
.addBoolean('$unsafe', {}, false), (value) => async (parse) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { $value, $fallback, $unsafe } = value;
try {
return await parse($value, { shouldFlatten: true });
}
catch (error) {
if (error instanceof core_1.Fallbackable || $unsafe) {
return parse($fallback, { shouldFlatten: true });
}
throw error;
}
}, { lazy: true }));
}
exports.tryDirective = tryDirective;
/** Checks a condition, uses then/else */
function ifDirective() {
return extension_utils_1.forKey('$if', extension_utils_1.validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema()
.addProperty('$check', SchemaBuilder.fromJsonSchema({}))
.addProperty('$then', SchemaBuilder.fromJsonSchema({}))
.addProperty('$else', SchemaBuilder.fromJsonSchema({})), (value) => async (parse) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { $check, $then, $else } = value;
const condition = (await parse($check)).toJSON();
if (condition) {
return parse($then, { shouldFlatten: true });
}
return parse($else, { shouldFlatten: true });
}, { lazy: true }));
}
exports.ifDirective = ifDirective;
/** Checks if two values are equal */
function eqDirective() {
return extension_utils_1.forKey('$eq', extension_utils_1.validateOptions((SchemaBuilder) => SchemaBuilder.arraySchema(SchemaBuilder.fromJsonSchema({})), (values) => async (parse) => {
for (const a of values) {
for (const b of values) {
if (a === b)
continue;
if (lodash_isequal_1.default(a, b))
continue;
return parse(false, { shouldFlatten: true });
}
}
return parse(true, { shouldFlatten: true });
}));
}
exports.eqDirective = eqDirective;
/** Prpoerties that are removed, used by references */
function hiddenDirective() {
return extension_utils_1.forKey('$hidden', () => async (parse) => {
return parse({}, { shouldMerge: true });
});
}
exports.hiddenDirective = hiddenDirective;
/** Uses another file as overriding values, layering them on top of current file */
function overrideDirective() {
return fileReferenceDirective('$override', { shouldOverride: true });
}
exports.overrideDirective = overrideDirective;
/** Uses another file as a "base", and extends on top of it */
function extendsDirective() {
return fileReferenceDirective('$extends', { shouldMerge: true });
}
exports.extendsDirective = extendsDirective;
/** Lookup a property in the same file, and "copy" it */
function extendsSelfDirective() {
const validate = extension_utils_1.validationFunction(({ stringSchema }) => stringSchema());
return extension_utils_1.forKey('$extendsSelf', (input, key, ctx) => async (parse, _, __, ___, root) => {
const value = (await parse(input)).toJSON();
validate(value, [...ctx, key]);
// we temporarily use a ParsedValue literal so that we get the same property lookup semantics
const selected = core_1.ParsedValue.literal(root).property(value.split('.'));
if (selected === undefined) {
throw new core_1.AppConfigError(`$extendsSelf selector was not found (${value})`);
}
if (selected.asObject() !== undefined) {
return parse(selected.toJSON(), { shouldMerge: true });
}
return parse(selected.toJSON(), { shouldFlatten: true });
});
}
exports.extendsSelfDirective = extendsSelfDirective;
/** Looks up an environment-specific value ($env) */
function envDirective(aliases = node_1.defaultAliases, environmentOverride, environmentSourceNames) {
const environment = environmentOverride !== null && environmentOverride !== void 0 ? environmentOverride : node_1.currentEnvironment(aliases, environmentSourceNames);
const metadata = { shouldOverride: true };
return extension_utils_1.forKey('$env', extension_utils_1.validateOptions((SchemaBuilder) => SchemaBuilder.emptySchema().addAdditionalProperties(), (value, _, ctx) => (parse) => {
if (!environment) {
if ('none' in value) {
return parse(value.none, metadata);
}
if ('default' in value) {
return parse(value.default, metadata);
}
throw new core_1.AppConfigError(`An $env directive was used (in ${extension_utils_1.keysToPath(ctx)}), but current environment (eg. NODE_ENV) is undefined`);
}
for (const [envName, envValue] of Object.entries(value)) {
if (envName === environment || aliases[envName] === environment) {
return parse(envValue, metadata);
}
}
if ('default' in value) {
return parse(value.default, metadata);
}
const found = Object.keys(value).join(', ');
throw new core_1.AppConfigError(`An $env directive was used (in ${extension_utils_1.keysToPath(ctx)}), but none matched the current environment (wanted ${environment}, saw [${found}])`);
},
// $env is lazy so that non-applicable envs don't get evaluated
{ lazy: true }));
}
exports.envDirective = envDirective;
/** Provides the current timestamp using { $timestamp: true } */
function timestampDirective(dateSource = () => new Date()) {
return extension_utils_1.forKey('$timestamp', extension_utils_1.validateOptions((SchemaBuilder) => SchemaBuilder.oneOf(SchemaBuilder.booleanSchema(), SchemaBuilder.emptySchema()
.addString('day', {}, false)
.addString('month', {}, false)
.addString('year', {}, false)
.addString('weekday', {}, false)
.addString('locale', {}, false)
.addString('timeZone', {}, false)
.addString('timeZoneName', {}, false)), (value) => (parse) => {
let formatted;
const date = dateSource();
if (value === true) {
formatted = date.toISOString();
}
else if (typeof value === 'object') {
const { locale } = value, options = __rest(value, ["locale"]);
formatted = date.toLocaleDateString(locale, options);
}
else {
throw new core_1.AppConfigError('$timestamp was provided an invalid option');
}
return parse(formatted, { shouldFlatten: true });
}));
}
exports.timestampDirective = timestampDirective;
/** Substitues environment variables */
function envVarDirective(aliases = node_1.defaultAliases, environmentOverride, environmentSourceNames) {
const envType = environmentOverride !== null && environmentOverride !== void 0 ? environmentOverride : node_1.currentEnvironment(aliases, environmentSourceNames);
return extension_utils_1.forKey('$envVar', (value, key, ctx) => async (parse) => {
let name;
let parseInt = false;
let parseFloat = false;
let parseBool = false;
if (typeof value === 'string') {
name = value;
}
else {
validateObject(value, [...ctx, key]);
if (Array.isArray(value))
throw new core_1.AppConfigError('$envVar was given an array');
const resolved = (await parse(value.name)).toJSON();
validateString(resolved, [...ctx, key, [core_1.InObject, 'name']]);
parseInt = !!(await parse(value.parseInt)).toJSON();
parseFloat = !!(await parse(value.parseFloat)).toJSON();
parseBool = !!(await parse(value.parseBool)).toJSON();
name = resolved;
}
const parseValue = (strValue) => {
if (parseInt) {
const parsed = Number.parseInt(strValue, 10);
if (Number.isNaN(parsed)) {
throw new core_1.AppConfigError(`Failed to parseInt(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
if (parseFloat) {
const parsed = Number.parseFloat(strValue);
if (Number.isNaN(parsed)) {
throw new core_1.AppConfigError(`Failed to parseFloat(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
if (parseBool) {
const parsed = strValue.toLowerCase() !== 'false' && strValue !== '0';
return parse(parsed, { shouldFlatten: true });
}
return parse(strValue, { shouldFlatten: true });
};
let resolvedValue = process.env[name];
if (!resolvedValue && name === 'APP_CONFIG_ENV') {
resolvedValue = envType;
}
if (resolvedValue) {
return parseValue(resolvedValue);
}
if (typeof value === 'object' && value.fallback !== undefined) {
const fallback = (await parse(value.fallback)).toJSON();
const allowNull = (await parse(value.allowNull)).toJSON();
if (allowNull) {
validateStringOrNull(fallback, [...ctx, key, [core_1.InObject, 'fallback']]);
}
else {
validateString(fallback, [...ctx, key, [core_1.InObject, 'fallback']]);
}
return parseValue(fallback);
}
throw new core_1.AppConfigError(`$envVar could not find ${name} environment variable`);
});
}
exports.envVarDirective = envVarDirective;
/** Substitues environment variables found in strings (similar to bash variable substitution) */
function substituteDirective(aliases = node_1.defaultAliases, environmentOverride, environmentSourceNames) {
const envType = environmentOverride !== null && environmentOverride !== void 0 ? environmentOverride : node_1.currentEnvironment(aliases, environmentSourceNames);
return extension_utils_1.forKey(['$substitute', '$subs'], (value, key, ctx) => async (parse) => {
if (typeof value === 'string') {
return parse(performAllSubstitutions(value, envType), { shouldFlatten: true });
}
validateObject(value, [...ctx, key]);
if (Array.isArray(value))
throw new core_1.AppConfigError('$substitute was given an array');
const name = (await parse(selectDefined(value.name, value.$name))).toJSON();
validateString(name, [...ctx, key, [core_1.InObject, 'name']]);
const parseValue = async (strValue) => {
const parseInt = (await parse(selectDefined(value.parseInt, value.$parseInt))).toJSON();
if (parseInt) {
const parsed = Number.parseInt(strValue, 10);
if (Number.isNaN(parsed)) {
throw new core_1.AppConfigError(`Failed to parseInt(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
const parseFloat = (await parse(selectDefined(value.parseFloat, value.$parseFloat))).toJSON();
if (parseFloat) {
const parsed = Number.parseFloat(strValue);
if (Number.isNaN(parsed)) {
throw new core_1.AppConfigError(`Failed to parseFloat(${strValue})`);
}
return parse(parsed, { shouldFlatten: true });
}
const parseBool = (await parse(selectDefined(value.parseBool, value.$parseBool))).toJSON();
if (parseBool) {
const parsed = strValue.toLowerCase() !== 'false' && strValue !== '0';
return parse(parsed, { shouldFlatten: true });
}
return parse(strValue, { shouldFlatten: true });
};
let resolvedValue = process.env[name];
if (!resolvedValue && name === 'APP_CONFIG_ENV') {
resolvedValue = envType;
}
if (resolvedValue) {
return parseValue(resolvedValue);
}
if (value.fallback !== undefined || value.$fallback !== undefined) {
const fallback = (await parse(selectDefined(value.fallback, value.$fallback))).toJSON();
const allowNull = (await parse(selectDefined(value.allowNull, value.$allowNull))).toJSON();
if (allowNull) {
validateStringOrNull(fallback, [...ctx, key, [core_1.InObject, 'fallback']]);
}
else {
validateString(fallback, [...ctx, key, [core_1.InObject, 'fallback']]);
}
return parseValue(fallback);
}
throw new core_1.AppConfigError(`$substitute could not find ${name} environment variable`);
});
}
exports.substituteDirective = substituteDirective;
exports.environmentVariableSubstitution = substituteDirective;
// common logic for $extends and $override
function fileReferenceDirective(keyName, meta) {
return extension_utils_1.forKey(keyName, extension_utils_1.validateOptions((SchemaBuilder) => {
const reference = SchemaBuilder.oneOf(SchemaBuilder.stringSchema(), SchemaBuilder.emptySchema()
.addString('path')
.addBoolean('optional', {}, false)
.addString('select', {}, false));
return SchemaBuilder.oneOf(reference, SchemaBuilder.arraySchema(reference));
}, (value) => async (_, __, context, extensions) => {
const retrieveFile = async (filepath, subselector, isOptional = false) => {
const resolvedPath = node_1.resolveFilepath(context, filepath);
logging_1.logger.verbose(`Loading file for ${keyName}: ${resolvedPath}`);
const source = new node_1.FileSource(resolvedPath);
const parsed = await source.read(extensions).catch((error) => {
if (error instanceof core_1.NotFoundError && isOptional) {
return core_1.ParsedValue.literal({});
}
throw error;
});
if (subselector) {
const found = parsed.property(subselector.split('.'));
if (!found) {
throw new core_1.FailedToSelectSubObject(`Failed to select ${subselector} in ${resolvedPath}`);
}
return found;
}
return parsed;
};
let parsed;
if (typeof value === 'string') {
parsed = await retrieveFile(value);
}
else if (Array.isArray(value)) {
parsed = core_1.ParsedValue.literal({});
for (const ext of value) {
if (typeof ext === 'string') {
parsed = core_1.ParsedValue.merge(parsed, await retrieveFile(ext));
}
else {
const { path, optional, select } = ext;
parsed = core_1.ParsedValue.merge(parsed, await retrieveFile(path, select, optional));
}
}
}
else {
const { path, optional, select } = value;
parsed = await retrieveFile(path, select, optional);
}
return parsed.assignMeta(meta);
}));
}
function performAllSubstitutions(text, envType) {
let output = text;
/* eslint-disable-next-line no-constant-condition */
while (true) {
// this regex matches:
// $FOO
// ${FOO}
// ${FOO:-fallback}
// ${FOO:-${FALLBACK}}
//
// var name is group 1 || 2
// fallback value is group 3
// https://regex101.com/r/6ZMmx7/3
const match = /\$(?:([a-zA-Z_]\w+)|(?:{([a-zA-Z_]\w+)(?::- *(.*?) *)?}))/g.exec(output);
if (!match)
break;
const fullMatch = match[0];
const varName = match[1] || match[2];
const fallback = match[3];
if (varName) {
const env = process.env[varName];
if (env !== undefined) {
output = output.replace(fullMatch, env);
}
else if (fallback !== undefined) {
// we'll recurse again, so that ${FOO:-${FALLBACK}} -> ${FALLBACK} -> value
output = performAllSubstitutions(output.replace(fullMatch, fallback), envType);
}
else if (varName === 'APP_CONFIG_ENV') {
if (!envType) {
throw new core_1.AppConfigError(`Could not find environment variable ${varName}`);
}
// there's a special case for APP_CONFIG_ENV, which is always the envType
output = output.replace(fullMatch, envType);
}
else {
throw new core_1.AppConfigError(`Could not find environment variable ${varName}`);
}
}
}
logging_1.logger.verbose(`Performed $substitute for "${text}" -> "${output}"`);
return output;
}
function selectDefined(...args) {
for (const a of args) {
if (a !== undefined)
return a;
}
return undefined;
}
const validateObject = extension_utils_1.validationFunction(({ emptySchema }) => emptySchema().addAdditionalProperties());
const validateString = extension_utils_1.validationFunction(({ stringSchema }) => stringSchema());
const validateStringOrNull = extension_utils_1.validationFunction(({ fromJsonSchema }) => fromJsonSchema({ type: ['null', 'string'] }));
//# sourceMappingURL=index.js.map
{
"name": "@app-config/extensions",
"description": "Common parsing extensions for @app-config",
"version": "2.4.6",
"version": "2.5.0",
"license": "MPL-2.0",

@@ -33,11 +33,11 @@ "author": {

"dependencies": {
"@app-config/core": "^2.4.6",
"@app-config/extension-utils": "^2.4.6",
"@app-config/logging": "^2.4.6",
"@app-config/node": "^2.4.6",
"@app-config/utils": "^2.4.6",
"@app-config/core": "^2.5.0",
"@app-config/extension-utils": "^2.5.0",
"@app-config/logging": "^2.5.0",
"@app-config/node": "^2.5.0",
"@app-config/utils": "^2.5.0",
"lodash.isequal": "4"
},
"devDependencies": {
"@app-config/test-utils": "^2.4.6",
"@app-config/test-utils": "^2.5.0",
"@types/lodash.isequal": "4"

@@ -44,0 +44,0 @@ },

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet