veffect
Version:
powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha
493 lines • 13 kB
JavaScript
/**
* @since 2.0.0
*/
import * as Equal from "./Equal.js";
import { dual } from "./Function.js";
import * as Hash from "./Hash.js";
import { NodeInspectSymbol } from "./Inspectable.js";
import * as Option from "./Option.js";
import * as order from "./Order.js";
import { pipeArguments } from "./Pipeable.js";
import { hasProperty, isBigInt, isNumber, isString } from "./Predicate.js";
const TypeId = /*#__PURE__*/Symbol.for("effect/Duration");
const bigint0 = /*#__PURE__*/BigInt(0);
const bigint24 = /*#__PURE__*/BigInt(24);
const bigint60 = /*#__PURE__*/BigInt(60);
const bigint1e3 = /*#__PURE__*/BigInt(1_000);
const bigint1e6 = /*#__PURE__*/BigInt(1_000_000);
const bigint1e9 = /*#__PURE__*/BigInt(1_000_000_000);
const DURATION_REGEX = /^(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)$/;
/**
* @since 2.0.0
*/
export const decode = input => {
if (isDuration(input)) {
return input;
} else if (isNumber(input)) {
return millis(input);
} else if (isBigInt(input)) {
return nanos(input);
} else if (Array.isArray(input)) {
if (input.length === 2 && isNumber(input[0]) && isNumber(input[1])) {
return nanos(BigInt(input[0]) * bigint1e9 + BigInt(input[1]));
}
} else if (isString(input)) {
DURATION_REGEX.lastIndex = 0; // Reset the lastIndex before each use
const match = DURATION_REGEX.exec(input);
if (match) {
const [_, valueStr, unit] = match;
const value = Number(valueStr);
switch (unit) {
case "nano":
case "nanos":
return nanos(BigInt(valueStr));
case "micro":
case "micros":
return micros(BigInt(valueStr));
case "milli":
case "millis":
return millis(value);
case "second":
case "seconds":
return seconds(value);
case "minute":
case "minutes":
return minutes(value);
case "hour":
case "hours":
return hours(value);
case "day":
case "days":
return days(value);
case "week":
case "weeks":
return weeks(value);
}
}
}
throw new Error("Invalid DurationInput");
};
/**
* @since 2.5.0
*/
export const decodeUnknown = /*#__PURE__*/Option.liftThrowable(decode);
const zeroValue = {
_tag: "Millis",
millis: 0
};
const infinityValue = {
_tag: "Infinity"
};
const DurationProto = {
[TypeId]: TypeId,
[Hash.symbol]() {
return Hash.cached(this, Hash.structure(this.value));
},
[Equal.symbol](that) {
return isDuration(that) && equals(this, that);
},
toString() {
return `Duration(${format(this)})`;
},
toJSON() {
switch (this.value._tag) {
case "Millis":
return {
_id: "Duration",
_tag: "Millis",
millis: this.value.millis
};
case "Nanos":
return {
_id: "Duration",
_tag: "Nanos",
hrtime: toHrTime(this)
};
case "Infinity":
return {
_id: "Duration",
_tag: "Infinity"
};
}
},
[NodeInspectSymbol]() {
return this.toJSON();
},
pipe() {
return pipeArguments(this, arguments);
}
};
const make = input => {
const duration = Object.create(DurationProto);
if (isNumber(input)) {
if (isNaN(input) || input <= 0) {
duration.value = zeroValue;
} else if (!Number.isFinite(input)) {
duration.value = infinityValue;
} else if (!Number.isInteger(input)) {
duration.value = {
_tag: "Nanos",
nanos: BigInt(Math.round(input * 1_000_000))
};
} else {
duration.value = {
_tag: "Millis",
millis: input
};
}
} else if (input <= bigint0) {
duration.value = zeroValue;
} else {
duration.value = {
_tag: "Nanos",
nanos: input
};
}
return duration;
};
/**
* @since 2.0.0
* @category guards
*/
export const isDuration = u => hasProperty(u, TypeId);
/**
* @since 2.0.0
* @category guards
*/
export const isFinite = self => self.value._tag !== "Infinity";
/**
* @since 2.0.0
* @category constructors
*/
export const zero = /*#__PURE__*/make(0);
/**
* @since 2.0.0
* @category constructors
*/
export const infinity = /*#__PURE__*/make(Infinity);
/**
* @since 2.0.0
* @category constructors
*/
export const nanos = nanos => make(nanos);
/**
* @since 2.0.0
* @category constructors
*/
export const micros = micros => make(micros * bigint1e3);
/**
* @since 2.0.0
* @category constructors
*/
export const millis = millis => make(millis);
/**
* @since 2.0.0
* @category constructors
*/
export const seconds = seconds => make(seconds * 1000);
/**
* @since 2.0.0
* @category constructors
*/
export const minutes = minutes => make(minutes * 60_000);
/**
* @since 2.0.0
* @category constructors
*/
export const hours = hours => make(hours * 3_600_000);
/**
* @since 2.0.0
* @category constructors
*/
export const days = days => make(days * 86_400_000);
/**
* @since 2.0.0
* @category constructors
*/
export const weeks = weeks => make(weeks * 604_800_000);
/**
* @since 2.0.0
* @category getters
*/
export const toMillis = self => {
const _self = decode(self);
switch (_self.value._tag) {
case "Infinity":
return Infinity;
case "Nanos":
return Number(_self.value.nanos) / 1_000_000;
case "Millis":
return _self.value.millis;
}
};
/**
* @since 2.0.0
* @category getters
*/
export const toSeconds = self => toMillis(self) / 1_000;
/**
* Get the duration in nanoseconds as a bigint.
*
* If the duration is infinite, returns `Option.none()`
*
* @since 2.0.0
* @category getters
*/
export const toNanos = self => {
const _self = decode(self);
switch (_self.value._tag) {
case "Infinity":
return Option.none();
case "Nanos":
return Option.some(_self.value.nanos);
case "Millis":
return Option.some(BigInt(Math.round(_self.value.millis * 1_000_000)));
}
};
/**
* Get the duration in nanoseconds as a bigint.
*
* If the duration is infinite, it throws an error.
*
* @since 2.0.0
* @category getters
*/
export const unsafeToNanos = self => {
const _self = decode(self);
switch (_self.value._tag) {
case "Infinity":
throw new Error("Cannot convert infinite duration to nanos");
case "Nanos":
return _self.value.nanos;
case "Millis":
return BigInt(Math.round(_self.value.millis * 1_000_000));
}
};
/**
* @since 2.0.0
* @category getters
*/
export const toHrTime = self => {
const _self = decode(self);
switch (_self.value._tag) {
case "Infinity":
return [Infinity, 0];
case "Nanos":
return [Number(_self.value.nanos / bigint1e9), Number(_self.value.nanos % bigint1e9)];
case "Millis":
return [Math.floor(_self.value.millis / 1000), Math.round(_self.value.millis % 1000 * 1_000_000)];
}
};
/**
* @since 2.0.0
* @category pattern matching
*/
export const match = /*#__PURE__*/dual(2, (self, options) => {
const _self = decode(self);
switch (_self.value._tag) {
case "Nanos":
return options.onNanos(_self.value.nanos);
case "Infinity":
return options.onMillis(Infinity);
case "Millis":
return options.onMillis(_self.value.millis);
}
});
/**
* @since 2.0.0
* @category pattern matching
*/
export const matchWith = /*#__PURE__*/dual(3, (self, that, options) => {
const _self = decode(self);
const _that = decode(that);
if (_self.value._tag === "Infinity" || _that.value._tag === "Infinity") {
return options.onMillis(toMillis(_self), toMillis(_that));
} else if (_self.value._tag === "Nanos" || _that.value._tag === "Nanos") {
const selfNanos = _self.value._tag === "Nanos" ? _self.value.nanos : BigInt(Math.round(_self.value.millis * 1_000_000));
const thatNanos = _that.value._tag === "Nanos" ? _that.value.nanos : BigInt(Math.round(_that.value.millis * 1_000_000));
return options.onNanos(selfNanos, thatNanos);
}
return options.onMillis(_self.value.millis, _that.value.millis);
});
/**
* @category instances
* @since 2.0.0
*/
export const Order = /*#__PURE__*/order.make((self, that) => matchWith(self, that, {
onMillis: (self, that) => self < that ? -1 : self > that ? 1 : 0,
onNanos: (self, that) => self < that ? -1 : self > that ? 1 : 0
}));
/**
* Checks if a `Duration` is between a `minimum` and `maximum` value.
*
* @category predicates
* @since 2.0.0
*/
export const between = /*#__PURE__*/order.between( /*#__PURE__*/order.mapInput(Order, decode));
/**
* @category instances
* @since 2.0.0
*/
export const Equivalence = (self, that) => matchWith(self, that, {
onMillis: (self, that) => self === that,
onNanos: (self, that) => self === that
});
const _min = /*#__PURE__*/order.min(Order);
/**
* @since 2.0.0
*/
export const min = /*#__PURE__*/dual(2, (self, that) => _min(decode(self), decode(that)));
const _max = /*#__PURE__*/order.max(Order);
/**
* @since 2.0.0
*/
export const max = /*#__PURE__*/dual(2, (self, that) => _max(decode(self), decode(that)));
const _clamp = /*#__PURE__*/order.clamp(Order);
/**
* @since 2.0.0
*/
export const clamp = /*#__PURE__*/dual(2, (self, options) => _clamp(decode(self), {
minimum: decode(options.minimum),
maximum: decode(options.maximum)
}));
/**
* @since 2.4.19
* @category math
*/
export const divide = /*#__PURE__*/dual(2, (self, by) => match(self, {
onMillis: millis => {
if (by === 0 || isNaN(by) || !Number.isFinite(by)) {
return Option.none();
}
return Option.some(make(millis / by));
},
onNanos: nanos => {
if (isNaN(by) || by <= 0 || !Number.isFinite(by)) {
return Option.none();
}
try {
return Option.some(make(nanos / BigInt(by)));
} catch (e) {
return Option.none();
}
}
}));
/**
* @since 2.4.19
* @category math
*/
export const unsafeDivide = /*#__PURE__*/dual(2, (self, by) => match(self, {
onMillis: millis => make(millis / by),
onNanos: nanos => {
if (isNaN(by) || by < 0 || Object.is(by, -0)) {
return zero;
} else if (Object.is(by, 0) || !Number.isFinite(by)) {
return infinity;
}
return make(nanos / BigInt(by));
}
}));
/**
* @since 2.0.0
* @category math
*/
export const times = /*#__PURE__*/dual(2, (self, times) => match(self, {
onMillis: millis => make(millis * times),
onNanos: nanos => make(nanos * BigInt(times))
}));
/**
* @since 2.0.0
* @category math
*/
export const subtract = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => make(self - that),
onNanos: (self, that) => make(self - that)
}));
/**
* @since 2.0.0
* @category math
*/
export const sum = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => make(self + that),
onNanos: (self, that) => make(self + that)
}));
/**
* @since 2.0.0
* @category predicates
*/
export const lessThan = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => self < that,
onNanos: (self, that) => self < that
}));
/**
* @since 2.0.0
* @category predicates
*/
export const lessThanOrEqualTo = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => self <= that,
onNanos: (self, that) => self <= that
}));
/**
* @since 2.0.0
* @category predicates
*/
export const greaterThan = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => self > that,
onNanos: (self, that) => self > that
}));
/**
* @since 2.0.0
* @category predicates
*/
export const greaterThanOrEqualTo = /*#__PURE__*/dual(2, (self, that) => matchWith(self, that, {
onMillis: (self, that) => self >= that,
onNanos: (self, that) => self >= that
}));
/**
* @since 2.0.0
* @category predicates
*/
export const equals = /*#__PURE__*/dual(2, (self, that) => Equivalence(decode(self), decode(that)));
/**
* Converts a `Duration` to a human readable string.
* @since 2.0.0
*
* @example
* import * as Duration from "effect/Duration"
*
* Duration.format(Duration.millis(1000)) // "1s"
* Duration.format(Duration.millis(1001)) // "1s 1ms"
*/
export const format = self => {
const duration = decode(self);
const parts = [];
if (duration.value._tag === "Infinity") {
return "Infinity";
}
const nanos = unsafeToNanos(duration);
if (nanos % bigint1e6) {
parts.push(`${nanos % bigint1e6}ns`);
}
const ms = nanos / bigint1e6;
if (ms % bigint1e3 !== bigint0) {
parts.push(`${ms % bigint1e3}ms`);
}
const sec = ms / bigint1e3;
if (sec % bigint60 !== bigint0) {
parts.push(`${sec % bigint60}s`);
}
const min = sec / bigint60;
if (min % bigint60 !== bigint0) {
parts.push(`${min % bigint60}m`);
}
const hr = min / bigint60;
if (hr % bigint24 !== bigint0) {
parts.push(`${hr % bigint24}h`);
}
const days = hr / bigint24;
if (days !== bigint0) {
parts.push(`${days}d`);
}
return parts.reverse().join(" ");
};
//# sourceMappingURL=Duration.js.map