shelving 1.48.1 → 1.49.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.48.1",
14
+ "version": "1.49.3",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -25,9 +25,9 @@
25
25
  "./db": "./db/index.js",
26
26
  "./error": "./error/index.js",
27
27
  "./feedback": "./feedback/index.js",
28
- "./firestore/client": "./firestore-client/index.js",
29
- "./firestore/lite": "./firestore-lite/index.js",
30
- "./firestore/server": "./firestore-server/index.js",
28
+ "./firestore/client": "./firestore/client/index.js",
29
+ "./firestore/lite": "./firestore/lite/index.js",
30
+ "./firestore/server": "./firestore/server/index.js",
31
31
  "./markup": "./markup/index.js",
32
32
  "./provider": "./provider/index.js",
33
33
  "./query": "./query/index.js",
@@ -1,4 +1,4 @@
1
- import { withProp, transformProps, NOERROR, LOADING, awaitNext, getData } from "../util/index.js";
1
+ import { withProp, transformData, NOERROR, LOADING, awaitNext, getData } from "../util/index.js";
2
2
  import { State } from "./State.js";
3
3
  /** State that stores a data object and has additional methods to help with that. */
4
4
  export class DataState extends State {
@@ -12,7 +12,7 @@ export class DataState extends State {
12
12
  }
13
13
  /** Update several props in this object. */
14
14
  update(updates) {
15
- this.next(transformProps(this.data, updates));
15
+ this.next(transformData(this.data, updates));
16
16
  }
17
17
  }
18
18
  /** State that stores an optional data object and has additional methods to help with that. */
@@ -35,7 +35,7 @@ export class ResultState extends State {
35
35
  }
36
36
  /** Update several props in this object. */
37
37
  update(updates) {
38
- this.next(transformProps(this.data, updates));
38
+ this.next(transformData(this.data, updates));
39
39
  }
40
40
  /** Delete this result. */
41
41
  delete() {
@@ -1,4 +1,4 @@
1
- import { transformProps, isNullish } from "../util/index.js";
1
+ import { transformData, isNullish } from "../util/index.js";
2
2
  import { Update } from "./Update.js";
3
3
  /** Update that can be applied to a data object to update its props. */
4
4
  export class DataUpdate extends Update {
@@ -11,7 +11,7 @@ export class DataUpdate extends Update {
11
11
  return new DataUpdate(!isNullish(key) ? { [key]: value } : {});
12
12
  }
13
13
  transform(existing) {
14
- return transformProps(existing, this.updates);
14
+ return transformData(existing, this.updates);
15
15
  }
16
16
  /** Return a data update with a specific prop marked for update. */
17
17
  with(key, value) {
package/util/date.d.ts CHANGED
@@ -1,17 +1,3 @@
1
- /** One second in millseconds. */
2
- export declare const SECOND = 1000;
3
- /** One minute in millseconds. */
4
- export declare const MINUTE: number;
5
- /** One hour in millseconds. */
6
- export declare const HOUR: number;
7
- /** One day in millseconds. */
8
- export declare const DAY: number;
9
- /** One week in millseconds. */
10
- export declare const WEEK: number;
11
- /** One month in millseconds. */
12
- export declare const MONTH: number;
13
- /** One year in millseconds. */
14
- export declare const YEAR: number;
15
1
  /** Is a value a date? */
16
2
  export declare const isDate: (v: unknown) => v is Date;
17
3
  /** Value that can possibly be converted to a `Date` instance. */
@@ -77,9 +63,9 @@ export declare const getWeeksUntil: (target: PossibleDate, current?: PossibleDat
77
63
  /** Count the number of weeks ago a date was. */
78
64
  export declare const getWeeksAgo: (target: PossibleDate, current?: PossibleDate | undefined) => number;
79
65
  /** Format a full description of a duration of time using the most reasonable units e.g. `5 years` or `1 week` or `4 minutes` or `12 milliseconds`. */
80
- export declare function formatFullDuration(ms: number): string;
66
+ export declare const formatFullDuration: (ms: number, maxPrecision?: number | undefined, minPrecision?: number | undefined) => string;
81
67
  /** Format a description of a duration of time using the most reasonable units e.g. `5y` or `4m` or `12ms`. */
82
- export declare function formatDuration(ms: number): string;
68
+ export declare const formatDuration: (ms: number, maxPrecision?: number | undefined, minPrecision?: number | undefined) => string;
83
69
  /**
84
70
  * Return full description of the gap between two dates, e.g. `in 10 days` or `2 hours ago`
85
71
  *
@@ -87,13 +73,6 @@ export declare function formatDuration(ms: number): string;
87
73
  * @param current Today's date (or a different date to measure from).
88
74
  */
89
75
  export declare function formatFullWhen(target: PossibleDate, current?: PossibleDate): string;
90
- /**
91
- * Return full description of when a date happened, e.g. `10 days` or `2 hours` or `-1 week`
92
- *
93
- * @param target The date when the thing will happen.
94
- * @param current Today's date (or a different date to measure from).
95
- */
96
- export declare const formatFullUntil: (target: PossibleDate, current?: PossibleDate | undefined) => string;
97
76
  /**
98
77
  * Return full description of when a date will happen, e.g. `10 days` or `2 hours` or `-1 week`
99
78
  *
package/util/date.js CHANGED
@@ -1,19 +1,5 @@
1
1
  import { AssertionError } from "../error/index.js";
2
- import { formatFullQuantity, formatQuantity } from "./number.js";
3
- /** One second in millseconds. */
4
- export const SECOND = 1000;
5
- /** One minute in millseconds. */
6
- export const MINUTE = 60 * SECOND;
7
- /** One hour in millseconds. */
8
- export const HOUR = 60 * MINUTE;
9
- /** One day in millseconds. */
10
- export const DAY = 24 * HOUR;
11
- /** One week in millseconds. */
12
- export const WEEK = 7 * DAY;
13
- /** One month in millseconds. */
14
- export const MONTH = 30 * DAY;
15
- /** One year in millseconds. */
16
- export const YEAR = 365 * DAY;
2
+ import { DAY, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR, formatUnits, formatFullUnits } from "./units.js";
17
3
  /** Is a value a date? */
18
4
  export const isDate = (v) => v instanceof Date;
19
5
  /**
@@ -124,40 +110,26 @@ export const getDaysAgo = (target, current) => 0 - getDaysUntil(target, current)
124
110
  export const getWeeksUntil = (target, current) => Math.floor(getDaysUntil(target, current) / 7);
125
111
  /** Count the number of weeks ago a date was. */
126
112
  export const getWeeksAgo = (target, current) => 0 - getWeeksUntil(target, current);
127
- /** Format a full description of a duration of time using the most reasonable units e.g. `5 years` or `1 week` or `4 minutes` or `12 milliseconds`. */
128
- export function formatFullDuration(ms) {
113
+ function _formatDuration(formatter, ms, maxPrecision = 0, minPrecision) {
129
114
  const abs = Math.abs(ms);
130
115
  if (abs <= 99 * SECOND)
131
- return formatFullQuantity(ms, "second", "seconds", 0); // Up to 99 seconds, e.g. '22 seconds ago'
116
+ return formatter(ms, "second", maxPrecision, minPrecision); // Up to 99 seconds, e.g. '22 seconds ago'
132
117
  if (abs <= HOUR)
133
- return formatFullQuantity(ms / MINUTE, "minute", "minutes", 0); // Up to one hour — show minutes, e.g. '18 minutes ago'
118
+ return formatter(ms / MINUTE, "minute", maxPrecision, minPrecision); // Up to one hour — show minutes, e.g. '18 minutes ago'
134
119
  if (abs <= DAY)
135
- return formatFullQuantity(ms / HOUR, "hour", "hours", 0); // Up to one day — show hours, e.g. '23 hours ago'
120
+ return formatter(ms / HOUR, "hour", maxPrecision, minPrecision); // Up to one day — show hours, e.g. '23 hours ago'
136
121
  if (abs <= 2 * WEEK)
137
- return formatFullQuantity(ms / DAY, "day", "days", 0); // Up to 2 weeks — show days, e.g. '13 days ago'
122
+ return formatter(ms / DAY, "day", maxPrecision, minPrecision); // Up to 2 weeks — show days, e.g. '13 days ago'
138
123
  if (abs <= 10 * WEEK)
139
- return formatFullQuantity(ms / WEEK, "week", "weeks", 0); // Up to 2 months — show weeks, e.g. '6 weeks ago'
124
+ return formatter(ms / WEEK, "week", maxPrecision, minPrecision); // Up to 2 months — show weeks, e.g. '6 weeks ago'
140
125
  if (abs <= 18 * MONTH)
141
- return formatFullQuantity(ms / MONTH, "month", "months", 0); // Up to 18 months — show months, e.g. '6 months ago'
142
- return formatFullQuantity(ms / YEAR, "year", "years", 0); // Above 18 months — show years, e.g. '2 years ago'
126
+ return formatter(ms / MONTH, "month", maxPrecision, minPrecision); // Up to 18 months — show months, e.g. '6 months ago'
127
+ return formatter(ms / YEAR, "year", maxPrecision, minPrecision); // Above 18 months — show years, e.g. '2 years ago'
143
128
  }
129
+ /** Format a full description of a duration of time using the most reasonable units e.g. `5 years` or `1 week` or `4 minutes` or `12 milliseconds`. */
130
+ export const formatFullDuration = (ms, maxPrecision, minPrecision) => _formatDuration(formatFullUnits, ms, maxPrecision, minPrecision);
144
131
  /** Format a description of a duration of time using the most reasonable units e.g. `5y` or `4m` or `12ms`. */
145
- export function formatDuration(ms) {
146
- const abs = Math.abs(ms);
147
- if (abs <= 99 * SECOND)
148
- return formatQuantity(ms, "s", 0); // Up to 99 seconds, e.g. '22 seconds ago'
149
- if (abs <= HOUR)
150
- return formatQuantity(ms / MINUTE, "m", 0); // Up to one hour — show minutes, e.g. '18 minutes ago'
151
- if (abs <= DAY)
152
- return formatQuantity(ms / HOUR, "h", 0); // Up to one day — show hours, e.g. '23 hours ago'
153
- if (abs <= 2 * WEEK)
154
- return formatQuantity(ms / DAY, "d", 0); // Up to 2 weeks — show days, e.g. '13 days ago'
155
- if (abs <= 10 * WEEK)
156
- return formatQuantity(ms / WEEK, "w", 0); // Up to 2 months — show weeks, e.g. '6 weeks ago'
157
- if (abs <= 18 * MONTH)
158
- return formatQuantity(ms / MONTH, "m", 0); // Up to 18 months — show months, e.g. '6 months ago'
159
- return formatQuantity(ms / YEAR, "y", 0); // Above 18 months — show years, e.g. '2 years ago'
160
- }
132
+ export const formatDuration = (ms, maxPrecision, minPrecision) => _formatDuration(formatUnits, ms, maxPrecision, minPrecision);
161
133
  /**
162
134
  * Return full description of the gap between two dates, e.g. `in 10 days` or `2 hours ago`
163
135
  *
@@ -170,13 +142,6 @@ export function formatFullWhen(target, current) {
170
142
  const duration = formatFullDuration(abs);
171
143
  return abs < 10 * SECOND ? "just now" : ms > 0 ? `in ${duration}` : `${duration} ago`;
172
144
  }
173
- /**
174
- * Return full description of when a date happened, e.g. `10 days` or `2 hours` or `-1 week`
175
- *
176
- * @param target The date when the thing will happen.
177
- * @param current Today's date (or a different date to measure from).
178
- */
179
- export const formatFullUntil = (target, current) => formatFullDuration(getDuration(target, current));
180
145
  /**
181
146
  * Return full description of when a date will happen, e.g. `10 days` or `2 hours` or `-1 week`
182
147
  *
@@ -10,9 +10,11 @@ export declare const PASSTHROUGH: <T>(value: T) => T;
10
10
  export declare const BLACKHOLE: (...args: Arguments) => void | undefined;
11
11
  /** Function that receives a dispatched value. */
12
12
  export declare type Dispatcher<T extends Arguments = []> = (...value: T) => void;
13
+ /** Function that receives a dispatched value. */
14
+ export declare type AsyncDispatcher<T extends Arguments = []> = (...value: T) => void | PromiseLike<void>;
13
15
  /** Safely dispatch a value to a dispatcher function. */
14
- export declare function dispatch<T extends Arguments>(dispatcher: Dispatcher<T>, ...value: T): void;
16
+ export declare function dispatch<T extends Arguments>(dispatcher: AsyncDispatcher<T>, ...value: T): void;
15
17
  /** Safely dispatch a value to a dispatcher method on an object. */
16
18
  export declare function dispatchMethod<T extends Arguments, M extends string | symbol>(obj: {
17
- [K in M]: Dispatcher<T>;
19
+ [K in M]: AsyncDispatcher<T>;
18
20
  }, key: M, ...value: T): void;
package/util/function.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isAsync } from "./async.js";
1
2
  import { logError } from "./error.js";
2
3
  /** Is a value a function? */
3
4
  export const isFunction = (v) => typeof v === "function";
@@ -8,7 +9,9 @@ export const BLACKHOLE = () => undefined;
8
9
  /** Safely dispatch a value to a dispatcher function. */
9
10
  export function dispatch(dispatcher, ...value) {
10
11
  try {
11
- dispatcher(...value);
12
+ const result = dispatcher(...value);
13
+ if (isAsync(result))
14
+ result.then(BLACKHOLE, logError);
12
15
  }
13
16
  catch (thrown) {
14
17
  logError(thrown);
@@ -17,7 +20,9 @@ export function dispatch(dispatcher, ...value) {
17
20
  /** Safely dispatch a value to a dispatcher method on an object. */
18
21
  export function dispatchMethod(obj, key, ...value) {
19
22
  try {
20
- obj[key](...value);
23
+ const result = obj[key](...value);
24
+ if (isAsync(result))
25
+ result.then(BLACKHOLE, logError);
21
26
  }
22
27
  catch (thrown) {
23
28
  logError(thrown);
package/util/number.d.ts CHANGED
@@ -59,9 +59,9 @@ export declare const truncateNumber: (num: number, precision?: number) => number
59
59
  * @returns The number formatted as a string in the browser's current locale.
60
60
  */
61
61
  export declare const formatNumber: (num: number, maxPrecision?: number, minPrecision?: number) => string;
62
- /** Format a number with a short suffix (number and suffix are separated by a non-breaking narrow space). */
62
+ /** Format a number with a short suffix. */
63
63
  export declare const formatQuantity: (num: number, suffix: string, maxPrecision?: number | undefined, minPrecision?: number | undefined) => string;
64
- /** Format a number with a longer full-word suffix (number and suffix are separated by a non-breaking space). */
64
+ /** Format a number with a longer full-word suffix. */
65
65
  export declare function formatFullQuantity(num: number, singular: string, plural: string, maxPrecision?: number, minPrecision?: number): string;
66
66
  /**
67
67
  * Cram a large whole numbers into a space efficient format, e.g. `14.7M`
@@ -103,6 +103,13 @@ export declare const isBetween: (num: number, start: number, end: number) => boo
103
103
  * @param end The end of the range, e.g. `20`
104
104
  */
105
105
  export declare const getBetween: (num: number, start: number, end: number) => number;
106
+ /**
107
+ * Get a number as a percentage of another number.
108
+ *
109
+ * @param numerator Number representing the amount of progress.
110
+ * @param denumerator The number representing the whole amount.
111
+ */
112
+ export declare const getPercent: (numerator: number, denumerator: number) => number;
106
113
  /** Sum an iterable set of numbers and return the total. */
107
114
  export declare function sumNumbers(nums: Iterable<number>): number;
108
115
  /** Find the number that's closest to a target in an iterable set of numbers. */
package/util/number.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { AssertionError } from "../error/index.js";
2
- import { NBSP, NNBSP } from "./string.js";
3
2
  // Constants.
4
3
  export const TRILLION = 1000000000000;
5
4
  export const BILLION = 1000000000;
@@ -18,7 +17,7 @@ export const isNumber = (v) => typeof v === "number";
18
17
  */
19
18
  export function toNumber(value) {
20
19
  if (typeof value === "number")
21
- return Number.isFinite(value) ? value : null;
20
+ return !Number.isFinite(value) ? null : value === 0 ? 0 : value;
22
21
  else if (typeof value === "string")
23
22
  return toNumber(parseFloat(value.replace(NUMERIC, "")));
24
23
  else if (value instanceof Date)
@@ -76,12 +75,12 @@ export const truncateNumber = (num, precision = 0) => Math.trunc(num * 10 ** pre
76
75
  * @returns The number formatted as a string in the browser's current locale.
77
76
  */
78
77
  export const formatNumber = (num, maxPrecision = 4, minPrecision = 0) => new Intl.NumberFormat(undefined, { maximumFractionDigits: maxPrecision, minimumFractionDigits: minPrecision }).format(num);
79
- /** Format a number with a short suffix (number and suffix are separated by a non-breaking narrow space). */
80
- export const formatQuantity = (num, suffix, maxPrecision, minPrecision) => `${formatNumber(num, maxPrecision, minPrecision)}${NNBSP}${suffix}`;
81
- /** Format a number with a longer full-word suffix (number and suffix are separated by a non-breaking space). */
78
+ /** Format a number with a short suffix. */
79
+ export const formatQuantity = (num, suffix, maxPrecision, minPrecision) => `${formatNumber(num, maxPrecision, minPrecision)}${suffix}`;
80
+ /** Format a number with a longer full-word suffix. */
82
81
  export function formatFullQuantity(num, singular, plural, maxPrecision, minPrecision) {
83
82
  const qty = formatNumber(num, maxPrecision, minPrecision);
84
- return `${qty}${NBSP}${qty === "1" ? singular : plural}`;
83
+ return `${qty} ${qty === "1" ? singular : plural}`;
85
84
  }
86
85
  /**
87
86
  * Cram a large whole numbers into a space efficient format, e.g. `14.7M`
@@ -138,6 +137,13 @@ export const isBetween = (num, start, end) => num >= start && num <= end;
138
137
  * @param end The end of the range, e.g. `20`
139
138
  */
140
139
  export const getBetween = (num, start, end) => Math.max(start, Math.min(end, num));
140
+ /**
141
+ * Get a number as a percentage of another number.
142
+ *
143
+ * @param numerator Number representing the amount of progress.
144
+ * @param denumerator The number representing the whole amount.
145
+ */
146
+ export const getPercent = (numerator, denumerator) => Math.max(0, Math.min(100, (100 / denumerator) * numerator));
141
147
  /** Sum an iterable set of numbers and return the total. */
142
148
  export function sumNumbers(nums) {
143
149
  let sum = 0;
@@ -1,4 +1,3 @@
1
- import type { Entry } from "./entry.js";
2
1
  import type { ArrayType, ImmutableArray } from "./array.js";
3
2
  import type { ImmutableMap } from "./map.js";
4
3
  import { ImmutableObject } from "./object.js";
@@ -35,12 +34,6 @@ export declare function yieldTransformed<I, O>(items: Iterable<I>, transformer:
35
34
  export declare function transformArray<T extends ImmutableArray>(arr: T, transformer: Transformer<ArrayType<T>, ArrayType<T>>): T;
36
35
  export declare function transformArray<I, O>(arr: Iterable<I>, transformer: (v: I) => O): ImmutableArray<O>;
37
36
  export declare function transformArray<I, O>(arr: Iterable<I>, transformer: Transformer<I, O>): ImmutableArray<O>;
38
- /**
39
- * Transform the _values_ of a set of entries using a transformer.
40
- * @yield Transformed entry after calling transforming the new value for each entry.
41
- */
42
- export declare function yieldTransformedValues<I, O>(entries: Iterable<Entry<I>>, transformer: (v: I) => O): Iterable<Entry<O>>;
43
- export declare function yieldTransformedValues<I, O>(entries: Iterable<Entry<I>>, transformer: Transformer<I, O>): Iterable<Entry<O>>;
44
37
  /**
45
38
  * Transform the _values_ of a map using a transformer.
46
39
  * @return New map after transforming its values.
@@ -65,7 +58,7 @@ export declare type PropTransformers<T extends Data> = {
65
58
  * Transform the props of a data object using a set of transformers for its props.
66
59
  * @returns New object with changed props (or the same object if no changes were made).
67
60
  */
68
- export declare function transformProps<T extends Data>(existing: T, transformers: PropTransformers<T>): T;
61
+ export declare function transformData<T extends Data>(existing: T, transformers: PropTransformers<T>): T;
69
62
  /** Set of named transformers for a a map-like object. */
70
63
  export declare type EntryTransformers<T> = ImmutableObject<Transformer<T | undefined, T>>;
71
64
  /** Transform some of the entries of a map-like object using a set of named transformers. */
package/util/transform.js CHANGED
@@ -16,18 +16,18 @@ export function* yieldTransformed(items, transformer) {
16
16
  export function transformArray(arr, transformer) {
17
17
  return Array.from(yieldTransformed(arr, transformer));
18
18
  }
19
- export function* yieldTransformedValues(entries, transformer) {
19
+ function* _yieldTransformedValues(entries, transformer) {
20
20
  for (const [k, v] of entries)
21
21
  yield [k, transform(v, transformer)];
22
22
  }
23
23
  export function transformMap(map, transformer) {
24
- return new Map(yieldTransformedValues(map, transformer));
24
+ return new Map(_yieldTransformedValues(map, transformer));
25
25
  }
26
26
  export function transformObject(obj, transformer) {
27
- return Object.fromEntries(yieldTransformedValues(Object.entries(obj), transformer));
27
+ return Object.fromEntries(_yieldTransformedValues(Object.entries(obj), transformer));
28
28
  }
29
29
  /** Apply transformers to the props of a data object and yield any props that changed. */
30
- function* yieldTransformedProps(existing, transformers) {
30
+ function* _yieldTransformedProps(existing, transformers) {
31
31
  for (const [k, v] of toProps(transformers))
32
32
  yield [k, transform(existing[k], v)];
33
33
  }
@@ -35,15 +35,15 @@ function* yieldTransformedProps(existing, transformers) {
35
35
  * Transform the props of a data object using a set of transformers for its props.
36
36
  * @returns New object with changed props (or the same object if no changes were made).
37
37
  */
38
- export function transformProps(existing, transformers) {
39
- return Object.fromEntries(yieldMerged(toProps(existing), yieldTransformedProps(existing, transformers)));
38
+ export function transformData(existing, transformers) {
39
+ return Object.fromEntries(yieldMerged(toProps(existing), _yieldTransformedProps(existing, transformers)));
40
40
  }
41
41
  /** Apply named transformers to the entries of a map-like object and yield any entries that changed. */
42
- function* yieldTransformedEntries(existing, updates) {
42
+ function* _yieldTransformedEntries(existing, updates) {
43
43
  for (const [k, t] of Object.entries(updates))
44
44
  yield [k, transform(existing[k], t)];
45
45
  }
46
46
  /** Transform some of the entries of a map-like object using a set of named transformers. */
47
47
  export function transformEntries(existing, updates, deletes) {
48
- return Object.fromEntries(yieldFiltered(yieldMerged(Object.entries(existing), yieldTransformedEntries(existing, updates)), isKeyInArray, deletes));
48
+ return Object.fromEntries(yieldFiltered(yieldMerged(Object.entries(existing), _yieldTransformedEntries(existing, updates)), isKeyInArray, deletes));
49
49
  }
package/util/undefined.js CHANGED
@@ -1,4 +1,4 @@
1
- import { RequiredError } from "..";
1
+ import { RequiredError } from "../error/index.js";
2
2
  /** Is a value undefined? */
3
3
  export const isUndefined = (v) => v === undefined;
4
4
  /** Is a value defined? */
package/util/units.d.ts CHANGED
@@ -1,9 +1,25 @@
1
+ /** One second in millseconds. */
2
+ export declare const SECOND = 1000;
3
+ /** One minute in millseconds. */
4
+ export declare const MINUTE: number;
5
+ /** One hour in millseconds. */
6
+ export declare const HOUR: number;
7
+ /** One day in millseconds. */
8
+ export declare const DAY: number;
9
+ /** One week in millseconds. */
10
+ export declare const WEEK: number;
11
+ /** One month in millseconds. */
12
+ export declare const MONTH: number;
13
+ /** One year in millseconds. */
14
+ export declare const YEAR: number;
1
15
  /** Valid information about a unit of measure. */
2
16
  export declare type UnitData = {
3
- /** Plural name for a unit, e.g. `feet` */
4
- readonly plural?: string;
5
17
  /** Type of a unit. */
6
18
  readonly type: UnitType;
19
+ /** Singular name for a unit, e.g. `foot` (only needed if different from reference). */
20
+ readonly singular?: string;
21
+ /** Plural name for a unit, e.g. `feet` */
22
+ readonly plural?: string;
7
23
  /** Short suffix for this unit, e.g. `km` */
8
24
  readonly suffix: string;
9
25
  /** All units must specify their 'base' unit, e.g. `meter` for for distance units and `liter` for volume units. */
@@ -14,7 +30,7 @@ export declare type UnitData = {
14
30
  /** Valid system of measurement reference. */
15
31
  export declare type UnitType = "percentage" | "angle" | "temperature" | "length" | "speed" | "pace" | "mass" | "time" | "volume";
16
32
  /** Valid unit of measurement reference (correspond to units allowed in `Intl.NumberFormat`, but not all). */
17
- export declare type UnitReference = "percent" | "degree" | "millimeter" | "centimeter" | "meter" | "kilometer" | "mile" | "yard" | "foot" | "inch" | "liter" | "milliliter" | "gallon" | "fluid-ounce" | "milligram" | "gram" | "kilogram" | "pound" | "stone" | "ounce" | "millisecond" | "second" | "minute" | "day" | "hour" | "week" | "month" | "year";
33
+ export declare type UnitReference = "percent" | "permille" | "permyriad" | "ppm" | "percentage-point" | "basis-point" | "degree" | "millimeter" | "centimeter" | "meter" | "kilometer" | "mile" | "yard" | "foot" | "inch" | "liter" | "milliliter" | "gallon" | "fluid-ounce" | "milligram" | "gram" | "kilogram" | "pound" | "stone" | "ounce" | "millisecond" | "second" | "minute" | "day" | "hour" | "week" | "month" | "year";
18
34
  /** List of units. */
19
35
  export declare const UNITS: {
20
36
  [K in UnitReference]: UnitData;
package/util/units.js CHANGED
@@ -1,37 +1,55 @@
1
1
  import { AssertionError } from "../error/index.js";
2
- import { DAY, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR } from "./date.js";
3
- import { formatFullQuantity, formatQuantity } from "./number.js";
2
+ import { formatFullQuantity, formatQuantity, MILLION } from "./number.js";
4
3
  import { NNBSP } from "./string.js";
4
+ /** One second in millseconds. */
5
+ export const SECOND = 1000;
6
+ /** One minute in millseconds. */
7
+ export const MINUTE = 60 * SECOND;
8
+ /** One hour in millseconds. */
9
+ export const HOUR = 60 * MINUTE;
10
+ /** One day in millseconds. */
11
+ export const DAY = 24 * HOUR;
12
+ /** One week in millseconds. */
13
+ export const WEEK = 7 * DAY;
14
+ /** One month in millseconds. */
15
+ export const MONTH = 30 * DAY;
16
+ /** One year in millseconds. */
17
+ export const YEAR = 365 * DAY;
5
18
  /** List of units. */
6
19
  export const UNITS = {
7
20
  "percent": { type: "percentage", base: 1, suffix: "%" },
8
- "degree": { type: "angle", base: 1, suffix: "deg" },
9
- "millimeter": { type: "length", base: 1, suffix: "mm" },
10
- "centimeter": { type: "length", base: 10, suffix: "cm" },
11
- "meter": { type: "length", base: 1000, centimeter: 100, millimeter: 1000, suffix: "m" },
12
- "kilometer": { type: "length", base: 1000000, centimeter: 100000, millimeter: 1000000, suffix: "km" },
13
- "inch": { type: "length", base: 25.4, suffix: "in" },
14
- "foot": { type: "length", base: 304.8, inch: 12, suffix: "ft", plural: "feet" },
15
- "yard": { type: "length", base: 914.4, inch: 36, foot: 3, suffix: "yd" },
16
- "mile": { type: "length", base: 1609344, yard: 1760, foot: 5280, inch: 63360, suffix: "mi" },
17
- "milliliter": { type: "volume", base: 1, suffix: "ml" },
18
- "liter": { type: "volume", base: 1000, suffix: "l" },
19
- "fluid-ounce": { type: "volume", base: 29.5735295625, gallon: 128, suffix: `fl${NNBSP}oz` },
20
- "gallon": { type: "volume", base: 3785.411784, suffix: "gal" },
21
- "milligram": { type: "mass", base: 1, suffix: "mg" },
22
- "gram": { type: "mass", base: 1000, suffix: "g" },
23
- "kilogram": { type: "mass", base: 1000000, suffix: "kg" },
24
- "ounce": { type: "mass", base: 28349.523125, pound: 0.0625, suffix: "oz" },
25
- "pound": { type: "mass", base: 453592.37, ounce: 16, suffix: "lb" },
26
- "stone": { type: "mass", base: 6350293.18, pound: 14, ounce: 224, suffix: "st", plural: "stone" },
27
- "millisecond": { type: "time", base: 1, suffix: "ms" },
28
- "second": { type: "time", base: SECOND, suffix: "s" },
29
- "minute": { type: "time", base: MINUTE, suffix: "m" },
30
- "hour": { type: "time", base: HOUR, suffix: "h" },
31
- "day": { type: "time", base: DAY, suffix: "d" },
32
- "week": { type: "time", base: WEEK, suffix: "w" },
33
- "month": { type: "time", base: MONTH, suffix: "m" },
34
- "year": { type: "time", base: YEAR, suffix: "y" },
21
+ "permille": { type: "percentage", base: 10, suffix: `${NNBSP}‰` },
22
+ "permyriad": { type: "percentage", base: 100, suffix: `${NNBSP}‱` },
23
+ "ppm": { type: "percentage", base: MILLION, suffix: `${NNBSP}ppm`, singular: "part per million", plural: "parts per million" },
24
+ "percentage-point": { type: "percentage", base: 1, suffix: `${NNBSP}pp`, singular: "percentage point", plural: "percentage points" },
25
+ "basis-point": { type: "percentage", base: 10000, suffix: `${NNBSP}bp`, singular: "basis point", plural: "basis points" },
26
+ "degree": { type: "angle", base: 1, suffix: `${NNBSP}deg` },
27
+ "millimeter": { type: "length", base: 1, suffix: `${NNBSP}mm` },
28
+ "centimeter": { type: "length", base: 10, suffix: `${NNBSP}cm` },
29
+ "meter": { type: "length", base: 1000, centimeter: 100, millimeter: 1000, suffix: `${NNBSP}m` },
30
+ "kilometer": { type: "length", base: 1000000, centimeter: 100000, millimeter: 1000000, suffix: `${NNBSP}km` },
31
+ "inch": { type: "length", base: 25.4, suffix: `${NNBSP}in` },
32
+ "foot": { type: "length", base: 304.8, inch: 12, suffix: `${NNBSP}ft`, plural: "feet" },
33
+ "yard": { type: "length", base: 914.4, inch: 36, foot: 3, suffix: `${NNBSP}yd` },
34
+ "mile": { type: "length", base: 1609344, yard: 1760, foot: 5280, inch: 63360, suffix: `${NNBSP}mi` },
35
+ "milliliter": { type: "volume", base: 1, suffix: `${NNBSP}ml` },
36
+ "liter": { type: "volume", base: 1000, suffix: `${NNBSP}l` },
37
+ "fluid-ounce": { type: "volume", base: 29.5735295625, gallon: 128, suffix: `${NNBSP}fl${NNBSP}oz`, singular: "fluid ounce", plural: "fluid ounces" },
38
+ "gallon": { type: "volume", base: 3785.411784, suffix: `${NNBSP}gal` },
39
+ "milligram": { type: "mass", base: 1, suffix: `${NNBSP}mg` },
40
+ "gram": { type: "mass", base: 1000, suffix: `${NNBSP}g` },
41
+ "kilogram": { type: "mass", base: 1000000, suffix: `${NNBSP}kg` },
42
+ "ounce": { type: "mass", base: 28349.523125, pound: 0.0625, suffix: `${NNBSP}oz` },
43
+ "pound": { type: "mass", base: 453592.37, ounce: 16, suffix: `${NNBSP}lb` },
44
+ "stone": { type: "mass", base: 6350293.18, pound: 14, ounce: 224, suffix: `${NNBSP}st`, plural: "stone" },
45
+ "millisecond": { type: "time", base: 1, suffix: `${NNBSP}ms` },
46
+ "second": { type: "time", base: SECOND, suffix: `${NNBSP}s` },
47
+ "minute": { type: "time", base: MINUTE, suffix: `${NNBSP}m` },
48
+ "hour": { type: "time", base: HOUR, suffix: `${NNBSP}h` },
49
+ "day": { type: "time", base: DAY, suffix: `${NNBSP}d` },
50
+ "week": { type: "time", base: WEEK, suffix: `${NNBSP}w` },
51
+ "month": { type: "time", base: MONTH, suffix: `${NNBSP}m` },
52
+ "year": { type: "time", base: YEAR, suffix: `${NNBSP}y` },
35
53
  };
36
54
  /** Convert between two units of the same type. */
37
55
  export function convertUnits(num, from, to) {
@@ -61,4 +79,4 @@ export const formatUnits = (num, unit, maxPrecision, minPrecision) => formatQuan
61
79
  * @param unit String reference for a unit of measure e.g. `kilometer`
62
80
  * @param maxPrecision Number of decimal places to round the number to e.g. `2`
63
81
  */
64
- export const formatFullUnits = (num, unit, maxPrecision, minPrecision) => formatFullQuantity(num, unit, UNITS[unit].plural || `${unit}s`, maxPrecision, minPrecision);
82
+ export const formatFullUnits = (num, unit, maxPrecision, minPrecision) => formatFullQuantity(num, UNITS[unit].singular || unit, UNITS[unit].plural || `${unit}s`, maxPrecision, minPrecision);
@@ -1,5 +1,5 @@
1
1
  import type { Entry } from "./entry.js";
2
- import { Data, Prop, Result } from "./data.js";
2
+ import { Data, Result } from "./data.js";
3
3
  /** Object that can validate an unknown value with its `validate()` method. */
4
4
  export interface Validatable<T> {
5
5
  /**
@@ -50,14 +50,6 @@ export declare function validateItems<T>(unsafeItems: Iterable<unknown>, validat
50
50
  * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
51
51
  */
52
52
  export declare function validateValues<T>(unsafeValues: Iterable<Entry>, validator: Validator<T>): Generator<Entry<T>, void>;
53
- /**
54
- * Validate a set of object props with a set of validators.
55
- *
56
- * @yield Valid entries for each specified validator.
57
- * @throw InvalidFeedback if one or more entries did not validate.
58
- * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
59
- */
60
- export declare function validateProps<T extends Data>(unsafeData: Data, validators: Validators<T>): Generator<Prop<T>, void>;
61
53
  /**
62
54
  * Validate an entire object with a set of validators.
63
55
  * - Defined props in the object will be validated against the corresponding validator.
@@ -69,7 +61,7 @@ export declare function validateProps<T extends Data>(unsafeData: Data, validato
69
61
  */
70
62
  export declare function validateData<T extends Data>(unsafeData: Data, validators: Validators<T>): T;
71
63
  /**
72
- * Validate a data result.
64
+ * Validate a data result against a validator for that data.
73
65
  * @return Valid object or `null`
74
66
  */
75
67
  export declare function validateResult<T extends Data>(unsafeResult: unknown, validator: Validator<T>): Result<T>;
package/util/validate.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Feedback, InvalidFeedback } from "../feedback/index.js";
2
2
  import { isData, toProps } from "./data.js";
3
+ import { isNullish } from "./null.js";
3
4
  /** Is a given value a validator? */
4
5
  export const isValidator = (v) => typeof v === "function" || (isData(v) && typeof v.validate === "function");
5
6
  /** Validate an unknown value with a validator. */
@@ -56,6 +57,18 @@ export function* validateValues(unsafeValues, validator) {
56
57
  if (invalid)
57
58
  throw new InvalidFeedback("Invalid items", details);
58
59
  }
60
+ /**
61
+ * Validate an entire object with a set of validators.
62
+ * - Defined props in the object will be validated against the corresponding validator.
63
+ * - `undefined` props in the object will be set to the default value of that prop.
64
+ *
65
+ * @return Valid object.
66
+ * @throw InvalidFeedback if one or more entries did not validate.
67
+ * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
68
+ */
69
+ export function validateData(unsafeData, validators) {
70
+ return Object.fromEntries(_yieldValidatedProps(unsafeData, validators));
71
+ }
59
72
  /**
60
73
  * Validate a set of object props with a set of validators.
61
74
  *
@@ -63,7 +76,7 @@ export function* validateValues(unsafeValues, validator) {
63
76
  * @throw InvalidFeedback if one or more entries did not validate.
64
77
  * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
65
78
  */
66
- export function* validateProps(unsafeData, validators) {
79
+ function* _yieldValidatedProps(unsafeData, validators) {
67
80
  let invalid = false;
68
81
  const details = {};
69
82
  for (const [k, validator] of toProps(validators)) {
@@ -83,21 +96,9 @@ export function* validateProps(unsafeData, validators) {
83
96
  throw new InvalidFeedback("Invalid data", details);
84
97
  }
85
98
  /**
86
- * Validate an entire object with a set of validators.
87
- * - Defined props in the object will be validated against the corresponding validator.
88
- * - `undefined` props in the object will be set to the default value of that prop.
89
- *
90
- * @return Valid object.
91
- * @throw InvalidFeedback if one or more entries did not validate.
92
- * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
93
- */
94
- export function validateData(unsafeData, validators) {
95
- return Object.fromEntries(validateProps(unsafeData, validators));
96
- }
97
- /**
98
- * Validate a data result.
99
+ * Validate a data result against a validator for that data.
99
100
  * @return Valid object or `null`
100
101
  */
101
102
  export function validateResult(unsafeResult, validator) {
102
- return unsafeResult === null || unsafeResult === undefined ? validate(unsafeResult, validator) : null;
103
+ return !isNullish(unsafeResult) ? validate(unsafeResult, validator) : null;
103
104
  }