Skip to content

Commit

Permalink
Add the arrayVar helper
Browse files Browse the repository at this point in the history
Change-type: minor
  • Loading branch information
thgreasi committed Sep 11, 2024
1 parent 6968a2a commit bcfc9cb
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 5 deletions.
40 changes: 35 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export const requiredVar = (varName: string | string[]): string => {
* this means that if no default is specified then `''` will be normalized to `undefined`
*/
export function optionalVar<R>(
varName: string | string[] | string[],
varName: string | string[],
defaultValue: R,
): string | R;
export function optionalVar(
varName: string | string[] | string[],
varName: string | string[],
defaultValue?: undefined,
): string | undefined;
export function optionalVar<R>(
varName: string | string[] | string[],
varName: string | string[],
defaultValue?: R,
): string | R | undefined {
// If there's a single string var name then fetch it directly from process.env, returning the default as necessary
Expand Down Expand Up @@ -99,7 +99,7 @@ export function intVar<R>(

const s = optionalVar(varName);
if (s == null) {
return defaultValue!;
return defaultValue;

Check failure on line 102 in src/index.ts

View workflow job for this annotation

GitHub Actions / Flowzone / Test npm (20.x)

Type 'R | undefined' is not assignable to type 'number | R'.
}

const i = checkInt(s);
Expand Down Expand Up @@ -128,7 +128,7 @@ export function boolVar<R>(

const s = optionalVar(varName);
if (s == null) {
return defaultValue!;
return defaultValue;

Check failure on line 131 in src/index.ts

View workflow job for this annotation

GitHub Actions / Flowzone / Test npm (20.x)

Type 'R | undefined' is not assignable to type 'boolean | R'.
}
if (s === 'false') {
return false;
Expand All @@ -141,6 +141,36 @@ export function boolVar<R>(
);
}

/**
* Splits an env var using the provided delimiter (defaults to ',') into an array of individual string values.
* Optionally accepts an array of allowed values for the array.
*/
export const arrayVar = <S extends string>(
name: string,
{
delimiter = ',',
allowedValues,
}: {
delimiter?: string | RegExp;
allowedValues?: S[];
} = {},
): S[] | undefined => {
const rawValue = optionalVar(name);
if (rawValue == null) {
return rawValue;
}
const items = rawValue.split(delimiter);

const allowedValueSet =
allowedValues != null ? new Set<string>(allowedValues) : null;
if (allowedValueSet != null && items.some((v) => !allowedValueSet.has(v))) {
throw new Error(
`'${name}' must be a '${delimiter.toString()}' delimited string with one or more of the allowed values: ${[...allowedValueSet].toString()}`,
);
}
return items as S[];
};

export type HostPort = { host: string; port: number };
/**
* Splits an env var in the format of `${host1}:${port1}, ${host2}:${port2}, ...`
Expand Down
35 changes: 35 additions & 0 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
requiredVar,
hostPortsVar,
trustProxyVar,
arrayVar,
} from '../src';
import type { HostPort } from '../src';

Expand Down Expand Up @@ -118,6 +119,40 @@ for (const [testType, varTest] of varTests) {
});
});

describe('arrayVar', () => {
if (typeof varTest !== 'string') {
return;
}
it('should return undefined array when the env var is missing', () => {
expect(arrayVar(varTest)).to.be.undefined;
});
it('should return an array of one item when there is no delimiter in the string', () => {
setVar('value1');
expect(arrayVar(varTest)).to.deep.equal(['value1']);
});
it('should return an array of two items when the default delimiter is part of the string', () => {
setVar('value1,value2,value3');
expect(arrayVar(varTest)).to.deep.equal(['value1', 'value2', 'value3']);
});
it('should include leading & trailing whitespaces in the array values', () => {
setVar(', value1 , value2 ');
expect(arrayVar(varTest)).to.deep.equal(['', ' value1 ', ' value2 ']);
});
it('should return an array of two items when the provided delimiter is part of the string', () => {
setVar('val,ue1;val,ue2;val,ue3');
expect(arrayVar(varTest, { delimiter: ';' })).to.deep.equal([
'val,ue1',
'val,ue2',
'val,ue3',
]);
});
it('should throw when the array includes items that are not part of the allowed values', () => {
setVar('value1,value2,value3');
expect(() => arrayVar(varTest, { allowedValues: ['value1', 'value2'] }))
.to.throw;
});
});

describe('splitHostPort', () => {
const defaultValue: HostPort[] = [];
it('should throw when the env var is missing', () => {
Expand Down

0 comments on commit bcfc9cb

Please sign in to comment.