Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,36 @@
"author": "Salesforce",
"license": "BSD-3-Clause",
"types": "lib/index.d.ts",
"exports": {
"./duration": {
"require": "./lib/duration.js",
"import": "./lib/duration.js",
"types": "./lib/duration.d.ts"
},
"./collections": {
"require": "./lib/collections.js",
"import": "./lib/collections.js",
"types": "./lib/collections.d.ts"
},
"./creatable": {
"require": "./lib/createable.js",
"import": "./lib/createable.js",
"types": "./lib/createable.d.ts"
},
"./env": {
"require": "./lib/env.js",
"import": "./lib/env.js",
"types": "./lib/env.d.ts"
},
".": {
"require": "./lib/index.js",
"import": "./lib/index.js",
"types": "./lib/index.d.ts"
}
},
"files": [
"lib/**/*.js",
"lib/**/*.d.ts",
"vendor/lodash.js"
"lib/**/*.d.ts"
],
"scripts": {
"build": "wireit",
Expand All @@ -22,7 +48,6 @@
"link-check": "wireit",
"lint": "wireit",
"lint-fix": "yarn sf-lint --fix",
"lodash": "./scripts/build-lodash.sh",
"prepack": "sf-prepack",
"prepare": "sf-install",
"test": "wireit",
Expand All @@ -34,7 +59,6 @@
},
"devDependencies": {
"@salesforce/dev-scripts": "^8.1.2",
"lodash-cli": "^4.17.5",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
Expand Down
25 changes: 0 additions & 25 deletions scripts/build-lodash.sh

This file was deleted.

20 changes: 7 additions & 13 deletions src/duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Optional } from '@salesforce/ts-types';
import type { Optional } from '@salesforce/ts-types';

/**
* A simple utility class for converting durations between minutes, seconds, and milliseconds.
Expand All @@ -14,35 +14,29 @@ export class Duration {
/**
* The number of milliseconds in one second.
*/
public static readonly MILLIS_IN_SECONDS: number = 1000;
public static readonly MILLIS_IN_SECONDS = 1000;

/**
* The number of seconds in one minute.
*/
public static readonly SECONDS_IN_MINUTE: number = 60;
public static readonly SECONDS_IN_MINUTE = 60;

/**
* The number of minutes in one hour.
*/
public static readonly MINUTES_IN_HOUR: number = 60;
public static readonly MINUTES_IN_HOUR = 60;

/**
* The number of hours in one day.
*/
public static readonly HOURS_IN_DAY: number = 24;
public static readonly HOURS_IN_DAY = 24;

/**
* The number of days in one week.
*/
public static readonly DAYS_IN_WEEK: number = 7;
public static readonly DAYS_IN_WEEK = 7;

public readonly quantity: number;
public readonly unit: Duration.Unit;

public constructor(quantity: number, unit: Duration.Unit = Duration.Unit.MINUTES) {
this.quantity = quantity;
this.unit = unit;
}
public constructor(public quantity: number, public unit: Duration.Unit = Duration.Unit.MINUTES) {}

/**
* Returns the current number of minutes represented by this `Duration` instance, rounded to the nearest integer
Expand Down
165 changes: 7 additions & 158 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { definiteEntriesOf, Dictionary, isKeyOf, KeyValue, Nullable, Optional, isNumber } from '@salesforce/ts-types';
import { InvalidDefaultEnvValueError } from './errors';
import { toNumber, toBoolean } from './nodash';
import { definiteEntriesOf, Dictionary, KeyValue, Nullable, Optional } from '@salesforce/ts-types';
import { toBoolean } from './shared/functions';

/**
* An injectable abstraction on top of `process.env` with various convenience functions
* for accessing environment variables of different anticipated shapes.
*/
export class Env {
public constructor(private store: Dictionary<string> = process?.env || {}) {
public constructor(private store: Dictionary<string> = process?.env ?? {}) {
this.store = store;
}

Expand All @@ -36,122 +35,6 @@ export class Env {
return this.store[key] ?? def;
}

/**
* Gets a `string` value from a finite set of expected values, matched case-insensitively.
*
* @param key The name of the envar.
* @param values The finite set of expected values.
*/
public getStringIn(key: string, values: string[]): Optional<string>;
/**
* Gets a `string` value from a finite set of expected values, matched case-insensitively, using a default if
* not found.
*
* @param key The name of the envar.
* @param values The finite set of expected values.
* @param def A default value.
* @throws {@link InvalidDefaultEnvValueError} If the provided default value is not a member of the expected set.
*/
public getStringIn(key: string, values: string[], def: string): string;
// underlying method
public getStringIn(key: string, values: string[], def?: string): Optional<string> {
const re = new RegExp(values.join('|'), 'i');
if (def && !re.test(def.toString())) {
const valueAsString = values.join(', ');
throw new InvalidDefaultEnvValueError(`${def} is not a member of ${valueAsString}`);
}
const value = this.getString(key);
if (!value) return def;
return re.test(value) ? value : def;
}

/**
* Gets a `string` value from a finite set of expected values derived from the keys of a given object of type `T`,
* matched case-insensitively. An optional `transform` may be provided that will preprocess both the found value
* and any provided default before testing for membership in the target object's key set.
*
* @param key The name of the envar.
* @param obj The object providing the keys to test with.
* @param transform A transform function applied to both the default and value before testing that
* either is a key of `T`.
*
* ```
* enum Mode { TEST = 'test', DEMO = 'demo' }
* env.setString('MY_ENVAR', Mode.DEMO);
* const check = env.getString('MY_ENVAR');
* // check -> 'demo'
* // typeof check -> string
* const value = env.getKeyOf('MY_ENVAR', Mode, v => v.toUpperCase());
* // value -> 'DEMO'
* // typeof value -> 'TEST' | 'DEMO' (i.e. Extract<keyof typeof Mode, string>)
* const enumValue = Mode[value];
* // enumValue -> 'demo'
* // typeof enumValue -> Mode
* ```
*/
public getKeyOf<T extends Record<string, unknown>>(
key: string,
obj: T,
transform?: (k: string) => string
): Optional<Extract<keyof T, string>>;
/**
* Gets a `string` value from a finite set of expected values derived from the keys of a given object of type `T`,
* matched case-insensitively, using a default if not found. An optional `transform` may be provided that will
* preprocess both the found value and any provided default before testing for membership in the target object's
* key set.
*
* @param key The name of the envar.
* @param obj The object providing the keys to test with.
* @param def A default value.
* @param transform A transform function applied to both the default and value before testing that
* either is a key of `T`.
* @param {@link InvalidDefaultEnvValueError} If the provided default value is not a member of the expected set.
*
* ```
* enum Mode { TEST = 'test', DEMO = 'demo' }
* env.setString('MY_ENVAR', Mode.DEMO);
* const check = env.getString('MY_ENVAR');
* // check -> 'demo'
* // typeof check -> string
* const value = env.getKeyOf('MY_ENVAR', Mode, Mode.TEST, v => v.toUpperCase());
* // value -> 'DEMO'
* // typeof value -> 'TEST' | 'DEMO' (Extract<keyof typeof Mode, string>)
* const enumValue = Mode[value];
* // enumValue -> 'demo'
* // typeof enumValue -> Mode
* ```
*/
public getKeyOf<T extends Record<string, unknown>>(
key: string,
obj: T,
def: string,
transform?: (k: string) => string
): Extract<keyof T, string>;
// underlying method
public getKeyOf<T extends Record<string, unknown>>(
key: string,
obj: T,
defOrTransform?: string | ((k: string) => string),
transform?: (k: string) => string
): Optional<Extract<keyof T, string>> {
let value: Optional<string>;
let def: Optional<string>;
if (typeof defOrTransform === 'function') {
transform = defOrTransform;
} else {
def = defOrTransform;
}
if (def === undefined) {
value = this.getStringIn(key, Object.keys(obj));
} else {
if (transform) def = transform(def);
value = this.getStringIn(key, Object.keys(obj), def);
}
if (!value) return;
if (typeof transform === 'function') value = transform(value);
if (isKeyOf(obj, value)) return value;
}

/**
* Sets a `string` value for a given key, or removes the current value when no value is given.
*
Expand All @@ -166,40 +49,6 @@ export class Env {
this.store[key] = value;
}

/**
* Gets a list of `string` values for a given key by splitting the raw value on `,` chars.
*
* @param key The name of the envar.
*/
public getList(key: string): Optional<string[]>;
/**
* Gets a list of `string` values for a given key by splitting the raw value on `,` chars.
*
* @param key The name of the envar.
* @param def A default list of values.
*/
public getList(key: string, def: string[]): string[];
// underlying method
public getList(key: string, def?: string[]): Optional<string[]> {
const value = this.getString(key);
return value ? value.split(',') : def;
}

/**
* Sets a `string` value from a list for a given key by joining values with a `,` into a raw `string` value,
* or removes the current value when no value is given.
*
* @param key The name of the envar.
* @param values The values to set.
*/
public setList(key: string, values: Nullable<string[]>): void {
if (values == null) {
this.unset(key);
return;
}
this.setString(key, values.join(','));
}

/**
* Gets a `boolean` value for a given key. Returns the default value if no value was found.
*
Expand Down Expand Up @@ -236,10 +85,10 @@ export class Env {
public getNumber(key: string, def?: number | undefined): Optional<number> {
const value = this.getString(key);
if (value) {
const num = toNumber(value);
return isNaN(num) && isNumber(def) ? def : num;
const num = Number(value);
return isNaN(num) && typeof def === 'number' ? def : num;
}
return isNumber(def) ? def : undefined;
return typeof def === 'number' ? def : undefined;
}

/**
Expand All @@ -253,7 +102,7 @@ export class Env {
this.unset(key);
return;
}
this.setString(key, isNumber(value) ? String(value) : value);
this.setString(key, typeof value === 'number' ? String(value) : value);
}

/**
Expand Down
14 changes: 1 addition & 13 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,13 @@ export class JsonParseError extends NamedError {
}

private static format(cause: Error, path?: string, line?: number, errorPortion?: string): string {
if (line == null) return cause.message || 'Unknown cause';
if (line == null) return cause.message ?? 'Unknown cause';
return `Parse error in file ${path ?? 'unknown'} on line ${line}\n${errorPortion ?? cause.message}`;
}
}

export class JsonStringifyError extends NamedError {
public constructor(cause: Error) {
super('JsonStringifyError', cause);
}
}

export class JsonDataFormatError extends NamedError {
public constructor(message: string) {
super('JsonDataFormatError', message);
}
}

export class InvalidDefaultEnvValueError extends NamedError {
public constructor(message: string) {
super('InvalidDefaultEnvValueError', message);
}
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export * from './duration';
export * from './env';
export * from './errors';
export * from './json';
export * from './nodash';
export * from './collections';
export * from './throttledPromiseAll';
export * from './settleAll';
Loading