@backstage/config
Advanced tools
Comparing version 0.0.0-nightly-20241119023621 to 0.0.0-nightly-20241206023738
# @backstage/config | ||
## 0.0.0-nightly-20241119023621 | ||
## 0.0.0-nightly-20241206023738 | ||
@@ -8,5 +8,17 @@ ### Patch Changes | ||
- Updated dependencies | ||
- @backstage/types@0.0.0-nightly-20241119023621 | ||
- @backstage/errors@0.0.0-nightly-20241119023621 | ||
- @backstage/errors@0.0.0-nightly-20241206023738 | ||
- @backstage/types@1.2.0 | ||
## 1.3.0 | ||
### Minor Changes | ||
- d52d7f9: Make `readDurationFromConfig` support both ISO and ms formats as well, to make it easier to enter time as an end user | ||
### Patch Changes | ||
- Updated dependencies | ||
- @backstage/types@1.2.0 | ||
- @backstage/errors@1.2.5 | ||
## 1.2.0 | ||
@@ -13,0 +25,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { JsonArray as JsonArray$1, JsonObject as JsonObject$1, JsonPrimitive as JsonPrimitive$1, JsonValue as JsonValue$1, HumanDuration } from '@backstage/types'; | ||
import { JsonPrimitive as JsonPrimitive$1, JsonObject as JsonObject$1, JsonArray as JsonArray$1, JsonValue as JsonValue$1, HumanDuration } from '@backstage/types'; | ||
import { Config as Config$1 } from '@backstage/config'; | ||
@@ -34,3 +34,3 @@ | ||
/** | ||
* Reads a duration from a config object. | ||
* Reads a duration from config. | ||
* | ||
@@ -40,2 +40,13 @@ * @public | ||
* | ||
* The supported formats are: | ||
* | ||
* - A string in the format of '1d', '2 seconds' etc. as supported by the `ms` | ||
* library. | ||
* - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'. | ||
* - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`. | ||
* | ||
* The string forms are naturally only supported if the `options.key` argument | ||
* is passed, since a `Config` argument always represents an object by its | ||
* nature. | ||
* | ||
* This does not support optionality; if you want to support optional durations, | ||
@@ -46,4 +57,4 @@ * you need to first check the presence of the target with `config.has(...)` and | ||
* @param config - A configuration object | ||
* @param key - If specified, read the duration from the given subkey | ||
* under the config object | ||
* @param key - If specified, read the duration from the given subkey under the | ||
* config object | ||
* @returns A duration object | ||
@@ -50,0 +61,0 @@ */ |
'use strict'; | ||
var errors = require('@backstage/errors'); | ||
var ms = require('ms'); | ||
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } | ||
var ms__default = /*#__PURE__*/_interopDefaultCompat(ms); | ||
const propsOfHumanDuration = [ | ||
@@ -16,2 +21,15 @@ "years", | ||
function readDurationFromConfig(config, options) { | ||
if (options?.key && typeof config.getOptional(options.key) === "string") { | ||
const value = config.getString(options.key).trim(); | ||
try { | ||
return value.startsWith("P") ? parseIsoDuration(value) : parseMsDuration(value); | ||
} catch (error) { | ||
throw new errors.InputError( | ||
`Invalid duration '${value}' in config at '${options.key}', ${errors.stringifyError(error)}` | ||
); | ||
} | ||
} | ||
return parseObjectDuration(config, options); | ||
} | ||
function parseObjectDuration(config, options) { | ||
let root; | ||
@@ -51,9 +69,131 @@ let found = false; | ||
} | ||
throw new errors.InputError(`${prefix}, ${error}`); | ||
throw new Error(`${prefix}, ${error}`); | ||
} | ||
return result; | ||
} | ||
function parseMsDuration(input) { | ||
if (/^\d+$/.exec(input)) { | ||
throw new Error( | ||
`The value cannot be a plain number; try adding a unit like 'ms' or 'seconds'` | ||
); | ||
} | ||
let milliseconds = ms__default.default(input); | ||
if (!Number.isFinite(milliseconds)) { | ||
throw new Error( | ||
`Not a valid duration string, try a number followed by a unit such as '1d' or '2 seconds'` | ||
); | ||
} else if (milliseconds < 0) { | ||
throw new Error("Negative durations are not allowed"); | ||
} else if (milliseconds === 0) { | ||
return { milliseconds: 0 }; | ||
} | ||
const s = 1e3; | ||
const m = s * 60; | ||
const h = m * 60; | ||
const d = h * 24; | ||
const w = d * 7; | ||
const y = d * 365.25; | ||
const result = {}; | ||
if (milliseconds >= y) { | ||
const years = Math.floor(milliseconds / y); | ||
milliseconds -= years * y; | ||
result.years = years; | ||
} | ||
if (milliseconds >= w) { | ||
const weeks = Math.floor(milliseconds / w); | ||
milliseconds -= weeks * w; | ||
result.weeks = weeks; | ||
} | ||
if (milliseconds >= d) { | ||
const days = Math.floor(milliseconds / d); | ||
milliseconds -= days * d; | ||
result.days = days; | ||
} | ||
if (milliseconds >= h) { | ||
const hours = Math.floor(milliseconds / h); | ||
milliseconds -= hours * h; | ||
result.hours = hours; | ||
} | ||
if (milliseconds >= m) { | ||
const minutes = Math.floor(milliseconds / m); | ||
milliseconds -= minutes * m; | ||
result.minutes = minutes; | ||
} | ||
if (milliseconds >= s) { | ||
const seconds = Math.floor(milliseconds / s); | ||
milliseconds -= seconds * s; | ||
result.seconds = seconds; | ||
} | ||
if (milliseconds > 0) { | ||
result.milliseconds = milliseconds; | ||
} | ||
return result; | ||
} | ||
function parseIsoDuration(input) { | ||
const match = /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/.exec( | ||
input | ||
); | ||
if (!match) { | ||
throw new Error( | ||
`Invalid ISO format, expected a value similar to 'P2DT6H' (2 days 6 hours) or 'PT1M' (1 minute)` | ||
); | ||
} | ||
const [ | ||
s, | ||
yearStr, | ||
monthStr, | ||
weekStr, | ||
dayStr, | ||
hourStr, | ||
minuteStr, | ||
secondStr, | ||
millisecondsStr | ||
] = match; | ||
const hasNegativePrefix = s[0] === "-"; | ||
const negativeSeconds = !!secondStr && secondStr[0] === "-"; | ||
const maybeNegate = (num, force = false) => num !== void 0 && (force || num && hasNegativePrefix) ? -num : num; | ||
const parseFloating = (value) => { | ||
if (typeof value === "undefined" || value === null || value === "") { | ||
return void 0; | ||
} | ||
return parseFloat(value); | ||
}; | ||
const parseMillis = (fraction) => { | ||
if (typeof fraction === "undefined" || fraction === null || fraction === "") { | ||
return void 0; | ||
} | ||
const f = parseFloat(`0.${fraction}`) * 1e3; | ||
return Math.floor(f); | ||
}; | ||
const years = maybeNegate(parseFloating(yearStr)); | ||
const months = maybeNegate(parseFloating(monthStr)); | ||
const weeks = maybeNegate(parseFloating(weekStr)); | ||
const days = maybeNegate(parseFloating(dayStr)); | ||
const hours = maybeNegate(parseFloating(hourStr)); | ||
const minutes = maybeNegate(parseFloating(minuteStr)); | ||
const seconds = maybeNegate(parseFloating(secondStr), secondStr === "-0"); | ||
const milliseconds = maybeNegate( | ||
parseMillis(millisecondsStr), | ||
negativeSeconds | ||
); | ||
if (years === void 0 && months === void 0 && weeks === void 0 && days === void 0 && hours === void 0 && minutes === void 0 && seconds === void 0 && milliseconds === void 0) { | ||
throw new Error("Invalid ISO format, no values given"); | ||
} | ||
return { | ||
...years ? { years } : {}, | ||
...months ? { months } : {}, | ||
...weeks ? { weeks } : {}, | ||
...days ? { days } : {}, | ||
...hours ? { hours } : {}, | ||
...minutes ? { minutes } : {}, | ||
...seconds ? { seconds } : {}, | ||
...milliseconds ? { milliseconds } : {} | ||
}; | ||
} | ||
exports.parseIsoDuration = parseIsoDuration; | ||
exports.parseMsDuration = parseMsDuration; | ||
exports.parseObjectDuration = parseObjectDuration; | ||
exports.propsOfHumanDuration = propsOfHumanDuration; | ||
exports.readDurationFromConfig = readDurationFromConfig; | ||
//# sourceMappingURL=readDurationFromConfig.cjs.js.map |
@@ -1,2 +0,3 @@ | ||
import { InputError } from '@backstage/errors'; | ||
import { InputError, stringifyError } from '@backstage/errors'; | ||
import ms from 'ms'; | ||
@@ -14,2 +15,15 @@ const propsOfHumanDuration = [ | ||
function readDurationFromConfig(config, options) { | ||
if (options?.key && typeof config.getOptional(options.key) === "string") { | ||
const value = config.getString(options.key).trim(); | ||
try { | ||
return value.startsWith("P") ? parseIsoDuration(value) : parseMsDuration(value); | ||
} catch (error) { | ||
throw new InputError( | ||
`Invalid duration '${value}' in config at '${options.key}', ${stringifyError(error)}` | ||
); | ||
} | ||
} | ||
return parseObjectDuration(config, options); | ||
} | ||
function parseObjectDuration(config, options) { | ||
let root; | ||
@@ -49,8 +63,127 @@ let found = false; | ||
} | ||
throw new InputError(`${prefix}, ${error}`); | ||
throw new Error(`${prefix}, ${error}`); | ||
} | ||
return result; | ||
} | ||
function parseMsDuration(input) { | ||
if (/^\d+$/.exec(input)) { | ||
throw new Error( | ||
`The value cannot be a plain number; try adding a unit like 'ms' or 'seconds'` | ||
); | ||
} | ||
let milliseconds = ms(input); | ||
if (!Number.isFinite(milliseconds)) { | ||
throw new Error( | ||
`Not a valid duration string, try a number followed by a unit such as '1d' or '2 seconds'` | ||
); | ||
} else if (milliseconds < 0) { | ||
throw new Error("Negative durations are not allowed"); | ||
} else if (milliseconds === 0) { | ||
return { milliseconds: 0 }; | ||
} | ||
const s = 1e3; | ||
const m = s * 60; | ||
const h = m * 60; | ||
const d = h * 24; | ||
const w = d * 7; | ||
const y = d * 365.25; | ||
const result = {}; | ||
if (milliseconds >= y) { | ||
const years = Math.floor(milliseconds / y); | ||
milliseconds -= years * y; | ||
result.years = years; | ||
} | ||
if (milliseconds >= w) { | ||
const weeks = Math.floor(milliseconds / w); | ||
milliseconds -= weeks * w; | ||
result.weeks = weeks; | ||
} | ||
if (milliseconds >= d) { | ||
const days = Math.floor(milliseconds / d); | ||
milliseconds -= days * d; | ||
result.days = days; | ||
} | ||
if (milliseconds >= h) { | ||
const hours = Math.floor(milliseconds / h); | ||
milliseconds -= hours * h; | ||
result.hours = hours; | ||
} | ||
if (milliseconds >= m) { | ||
const minutes = Math.floor(milliseconds / m); | ||
milliseconds -= minutes * m; | ||
result.minutes = minutes; | ||
} | ||
if (milliseconds >= s) { | ||
const seconds = Math.floor(milliseconds / s); | ||
milliseconds -= seconds * s; | ||
result.seconds = seconds; | ||
} | ||
if (milliseconds > 0) { | ||
result.milliseconds = milliseconds; | ||
} | ||
return result; | ||
} | ||
function parseIsoDuration(input) { | ||
const match = /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/.exec( | ||
input | ||
); | ||
if (!match) { | ||
throw new Error( | ||
`Invalid ISO format, expected a value similar to 'P2DT6H' (2 days 6 hours) or 'PT1M' (1 minute)` | ||
); | ||
} | ||
const [ | ||
s, | ||
yearStr, | ||
monthStr, | ||
weekStr, | ||
dayStr, | ||
hourStr, | ||
minuteStr, | ||
secondStr, | ||
millisecondsStr | ||
] = match; | ||
const hasNegativePrefix = s[0] === "-"; | ||
const negativeSeconds = !!secondStr && secondStr[0] === "-"; | ||
const maybeNegate = (num, force = false) => num !== void 0 && (force || num && hasNegativePrefix) ? -num : num; | ||
const parseFloating = (value) => { | ||
if (typeof value === "undefined" || value === null || value === "") { | ||
return void 0; | ||
} | ||
return parseFloat(value); | ||
}; | ||
const parseMillis = (fraction) => { | ||
if (typeof fraction === "undefined" || fraction === null || fraction === "") { | ||
return void 0; | ||
} | ||
const f = parseFloat(`0.${fraction}`) * 1e3; | ||
return Math.floor(f); | ||
}; | ||
const years = maybeNegate(parseFloating(yearStr)); | ||
const months = maybeNegate(parseFloating(monthStr)); | ||
const weeks = maybeNegate(parseFloating(weekStr)); | ||
const days = maybeNegate(parseFloating(dayStr)); | ||
const hours = maybeNegate(parseFloating(hourStr)); | ||
const minutes = maybeNegate(parseFloating(minuteStr)); | ||
const seconds = maybeNegate(parseFloating(secondStr), secondStr === "-0"); | ||
const milliseconds = maybeNegate( | ||
parseMillis(millisecondsStr), | ||
negativeSeconds | ||
); | ||
if (years === void 0 && months === void 0 && weeks === void 0 && days === void 0 && hours === void 0 && minutes === void 0 && seconds === void 0 && milliseconds === void 0) { | ||
throw new Error("Invalid ISO format, no values given"); | ||
} | ||
return { | ||
...years ? { years } : {}, | ||
...months ? { months } : {}, | ||
...weeks ? { weeks } : {}, | ||
...days ? { days } : {}, | ||
...hours ? { hours } : {}, | ||
...minutes ? { minutes } : {}, | ||
...seconds ? { seconds } : {}, | ||
...milliseconds ? { milliseconds } : {} | ||
}; | ||
} | ||
export { propsOfHumanDuration, readDurationFromConfig }; | ||
export { parseIsoDuration, parseMsDuration, parseObjectDuration, propsOfHumanDuration, readDurationFromConfig }; | ||
//# sourceMappingURL=readDurationFromConfig.esm.js.map |
{ | ||
"name": "@backstage/config", | ||
"version": "0.0.0-nightly-20241119023621", | ||
"version": "0.0.0-nightly-20241206023738", | ||
"description": "Config API used by Backstage core, backend, and CLI", | ||
@@ -39,8 +39,9 @@ "backstage": { | ||
"dependencies": { | ||
"@backstage/errors": "0.0.0-nightly-20241119023621", | ||
"@backstage/types": "0.0.0-nightly-20241119023621" | ||
"@backstage/errors": "0.0.0-nightly-20241206023738", | ||
"@backstage/types": "1.2.0", | ||
"ms": "^2.1.3" | ||
}, | ||
"devDependencies": { | ||
"@backstage/cli": "0.0.0-nightly-20241119023621", | ||
"@backstage/test-utils": "0.0.0-nightly-20241119023621" | ||
"@backstage/cli": "0.0.0-nightly-20241206023738", | ||
"@backstage/test-utils": "0.0.0-nightly-20241206023738" | ||
}, | ||
@@ -47,0 +48,0 @@ "typesVersions": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
141095
1369
3
+ Addedms@^2.1.3
+ Added@backstage/errors@0.0.0-nightly-20241206023738(transitive)
+ Added@backstage/types@1.2.0(transitive)
+ Addedms@2.1.3(transitive)
- Removed@backstage/errors@0.0.0-nightly-20241119023621(transitive)
- Removed@backstage/types@0.0.0-nightly-20241119023621(transitive)
Updated@backstage/types@1.2.0