@choksheak/ts-utils 0.1.8 → 0.1.9

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/dateTimeStr.d.ts CHANGED
@@ -1,14 +1,8 @@
1
- export declare class DateTimeStr {
2
- static yyyyMmDd(dt?: Date, separator?: string): string;
3
- static hhMmSs(dt?: Date, separator?: string): string;
4
- static hhMmSsMs(dt?: Date, timeSeparator?: string, msSeparator?: string): string;
5
- static tz(dt?: Date): string;
6
- /** Full local date/time string. */
7
- static local(dt?: Date, dateSeparator?: string, dtSeparator?: string, timeSeparator?: string, msSeparator?: string): string;
8
- /** Use the default ISO string function to keep things simple here. */
9
- static utc(dt?: Date): string;
10
- /** Default full local date+time string with full & concise info. */
11
- static get now(): string;
12
- /** Default full UTC date+time string with full & concise info. */
13
- static get utcNow(): string;
14
- }
1
+ export declare function yyyyMmDd(dt?: Date, separator?: string): string;
2
+ export declare function hhMmSs(dt?: Date, separator?: string): string;
3
+ export declare function hhMmSsMs(dt?: Date, timeSeparator?: string, msSeparator?: string): string;
4
+ export declare function tzShort(dt?: Date): string;
5
+ export declare function getLongMonthNameZeroIndexed(month: number, locales?: Intl.LocalesArgument): string;
6
+ export declare function getLongMonthNameOneIndexed(month: number, locales?: Intl.LocalesArgument): string;
7
+ export declare function getShortMonthNameZeroIndexed(month: number, locales?: Intl.LocalesArgument): string;
8
+ export declare function getShortMonthNameOneIndexed(month: number, locales?: Intl.LocalesArgument): string;
package/dateTimeStr.js CHANGED
@@ -20,52 +20,64 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/dateTimeStr.ts
21
21
  var dateTimeStr_exports = {};
22
22
  __export(dateTimeStr_exports, {
23
- DateTimeStr: () => DateTimeStr
23
+ getLongMonthNameOneIndexed: () => getLongMonthNameOneIndexed,
24
+ getLongMonthNameZeroIndexed: () => getLongMonthNameZeroIndexed,
25
+ getShortMonthNameOneIndexed: () => getShortMonthNameOneIndexed,
26
+ getShortMonthNameZeroIndexed: () => getShortMonthNameZeroIndexed,
27
+ hhMmSs: () => hhMmSs,
28
+ hhMmSsMs: () => hhMmSsMs,
29
+ tzShort: () => tzShort,
30
+ yyyyMmDd: () => yyyyMmDd
24
31
  });
25
32
  module.exports = __toCommonJS(dateTimeStr_exports);
26
- var DateTimeStr = class _DateTimeStr {
27
- static yyyyMmDd(dt = /* @__PURE__ */ new Date(), separator = "-") {
28
- const yr = dt.getFullYear();
29
- const mth = dt.getMonth() + 1;
30
- const day = dt.getDate();
31
- return yr + separator + (mth < 10 ? "0" + mth : mth) + separator + (day < 10 ? "0" + day : day);
33
+ function yyyyMmDd(dt = /* @__PURE__ */ new Date(), separator = "-") {
34
+ const yr = dt.getFullYear();
35
+ const mth = dt.getMonth() + 1;
36
+ const day = dt.getDate();
37
+ return yr + separator + (mth < 10 ? "0" + mth : mth) + separator + (day < 10 ? "0" + day : day);
38
+ }
39
+ function hhMmSs(dt = /* @__PURE__ */ new Date(), separator = ":") {
40
+ const hr = dt.getHours();
41
+ const min = dt.getMinutes();
42
+ const sec = dt.getSeconds();
43
+ return (hr < 10 ? "0" + hr : hr) + separator + (min < 10 ? "0" + min : min) + separator + (sec < 10 ? "0" + sec : sec);
44
+ }
45
+ function hhMmSsMs(dt = /* @__PURE__ */ new Date(), timeSeparator = ":", msSeparator = ".") {
46
+ const ms = dt.getMilliseconds();
47
+ return hhMmSs(dt, timeSeparator) + msSeparator + (ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms);
48
+ }
49
+ function tzShort(dt = /* @__PURE__ */ new Date()) {
50
+ if (dt.getTimezoneOffset() === 0) {
51
+ return "Z";
32
52
  }
33
- static hhMmSs(dt = /* @__PURE__ */ new Date(), separator = ":") {
34
- const hr = dt.getHours();
35
- const min = dt.getMinutes();
36
- const sec = dt.getSeconds();
37
- return (hr < 10 ? "0" + hr : hr) + separator + (min < 10 ? "0" + min : min) + separator + (sec < 10 ? "0" + sec : sec);
38
- }
39
- static hhMmSsMs(dt = /* @__PURE__ */ new Date(), timeSeparator = ":", msSeparator = ".") {
40
- const ms = dt.getMilliseconds();
41
- return _DateTimeStr.hhMmSs(dt, timeSeparator) + msSeparator + (ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms);
42
- }
43
- static tz(dt = /* @__PURE__ */ new Date()) {
44
- if (dt.getTimezoneOffset() === 0) {
45
- return "Z";
46
- }
47
- const tzHours = dt.getTimezoneOffset() / 60;
48
- return tzHours >= 0 ? "+" + tzHours : String(tzHours);
49
- }
50
- /** Full local date/time string. */
51
- static local(dt = /* @__PURE__ */ new Date(), dateSeparator = "-", dtSeparator = " ", timeSeparator = ":", msSeparator = ".") {
52
- return _DateTimeStr.yyyyMmDd(dt, dateSeparator) + dtSeparator + _DateTimeStr.hhMmSsMs(dt, timeSeparator, msSeparator) + _DateTimeStr.tz(dt);
53
- }
54
- /** Use the default ISO string function to keep things simple here. */
55
- static utc(dt = /* @__PURE__ */ new Date()) {
56
- return dt.toISOString();
57
- }
58
- /** Default full local date+time string with full & concise info. */
59
- static get now() {
60
- return _DateTimeStr.local();
61
- }
62
- /** Default full UTC date+time string with full & concise info. */
63
- static get utcNow() {
64
- return _DateTimeStr.utc();
65
- }
66
- };
53
+ const tzHours = dt.getTimezoneOffset() / 60;
54
+ return tzHours >= 0 ? "+" + tzHours : String(tzHours);
55
+ }
56
+ function getLongMonthNameZeroIndexed(month, locales = "default") {
57
+ return new Date(2024, month, 15).toLocaleString(locales, {
58
+ month: "long"
59
+ });
60
+ }
61
+ function getLongMonthNameOneIndexed(month, locales = "default") {
62
+ return getLongMonthNameZeroIndexed(month - 1, locales);
63
+ }
64
+ function getShortMonthNameZeroIndexed(month, locales = "default") {
65
+ return new Date(2e3, month, 15).toLocaleString(locales, {
66
+ month: "short"
67
+ });
68
+ }
69
+ function getShortMonthNameOneIndexed(month, locales = "default") {
70
+ return getShortMonthNameZeroIndexed(month - 1, locales);
71
+ }
67
72
  // Annotate the CommonJS export names for ESM import in node:
68
73
  0 && (module.exports = {
69
- DateTimeStr
74
+ getLongMonthNameOneIndexed,
75
+ getLongMonthNameZeroIndexed,
76
+ getShortMonthNameOneIndexed,
77
+ getShortMonthNameZeroIndexed,
78
+ hhMmSs,
79
+ hhMmSsMs,
80
+ tzShort,
81
+ yyyyMmDd
70
82
  });
71
83
  //# sourceMappingURL=dateTimeStr.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dateTimeStr.ts"],"sourcesContent":["export class DateTimeStr {\n public static yyyyMmDd(dt = new Date(), separator = \"-\"): string {\n const yr = dt.getFullYear();\n const mth = dt.getMonth() + 1;\n const day = dt.getDate();\n\n return (\n yr +\n separator +\n (mth < 10 ? \"0\" + mth : mth) +\n separator +\n (day < 10 ? \"0\" + day : day)\n );\n }\n\n public static hhMmSs(dt = new Date(), separator = \":\"): string {\n const hr = dt.getHours();\n const min = dt.getMinutes();\n const sec = dt.getSeconds();\n\n return (\n (hr < 10 ? \"0\" + hr : hr) +\n separator +\n (min < 10 ? \"0\" + min : min) +\n separator +\n (sec < 10 ? \"0\" + sec : sec)\n );\n }\n\n public static hhMmSsMs(\n dt = new Date(),\n timeSeparator = \":\",\n msSeparator = \".\",\n ): string {\n const ms = dt.getMilliseconds();\n\n return (\n DateTimeStr.hhMmSs(dt, timeSeparator) +\n msSeparator +\n (ms < 10 ? \"00\" + ms : ms < 100 ? \"0\" + ms : ms)\n );\n }\n\n public static tz(dt = new Date()): string {\n if (dt.getTimezoneOffset() === 0) {\n return \"Z\";\n }\n\n const tzHours = dt.getTimezoneOffset() / 60;\n return tzHours >= 0 ? \"+\" + tzHours : String(tzHours);\n }\n\n /** Full local date/time string. */\n public static local(\n dt = new Date(),\n dateSeparator = \"-\",\n dtSeparator = \" \",\n timeSeparator = \":\",\n msSeparator = \".\",\n ): string {\n return (\n DateTimeStr.yyyyMmDd(dt, dateSeparator) +\n dtSeparator +\n DateTimeStr.hhMmSsMs(dt, timeSeparator, msSeparator) +\n DateTimeStr.tz(dt)\n );\n }\n\n /** Use the default ISO string function to keep things simple here. */\n public static utc(dt = new Date()) {\n return dt.toISOString();\n }\n\n /** Default full local date+time string with full & concise info. */\n public static get now() {\n return DateTimeStr.local();\n }\n\n /** Default full UTC date+time string with full & concise info. */\n public static get utcNow() {\n return DateTimeStr.utc();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,IAAM,cAAN,MAAM,aAAY;AAAA,EACvB,OAAc,SAAS,KAAK,oBAAI,KAAK,GAAG,YAAY,KAAa;AAC/D,UAAM,KAAK,GAAG,YAAY;AAC1B,UAAM,MAAM,GAAG,SAAS,IAAI;AAC5B,UAAM,MAAM,GAAG,QAAQ;AAEvB,WACE,KACA,aACC,MAAM,KAAK,MAAM,MAAM,OACxB,aACC,MAAM,KAAK,MAAM,MAAM;AAAA,EAE5B;AAAA,EAEA,OAAc,OAAO,KAAK,oBAAI,KAAK,GAAG,YAAY,KAAa;AAC7D,UAAM,KAAK,GAAG,SAAS;AACvB,UAAM,MAAM,GAAG,WAAW;AAC1B,UAAM,MAAM,GAAG,WAAW;AAE1B,YACG,KAAK,KAAK,MAAM,KAAK,MACtB,aACC,MAAM,KAAK,MAAM,MAAM,OACxB,aACC,MAAM,KAAK,MAAM,MAAM;AAAA,EAE5B;AAAA,EAEA,OAAc,SACZ,KAAK,oBAAI,KAAK,GACd,gBAAgB,KAChB,cAAc,KACN;AACR,UAAM,KAAK,GAAG,gBAAgB;AAE9B,WACE,aAAY,OAAO,IAAI,aAAa,IACpC,eACC,KAAK,KAAK,OAAO,KAAK,KAAK,MAAM,MAAM,KAAK;AAAA,EAEjD;AAAA,EAEA,OAAc,GAAG,KAAK,oBAAI,KAAK,GAAW;AACxC,QAAI,GAAG,kBAAkB,MAAM,GAAG;AAChC,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,GAAG,kBAAkB,IAAI;AACzC,WAAO,WAAW,IAAI,MAAM,UAAU,OAAO,OAAO;AAAA,EACtD;AAAA;AAAA,EAGA,OAAc,MACZ,KAAK,oBAAI,KAAK,GACd,gBAAgB,KAChB,cAAc,KACd,gBAAgB,KAChB,cAAc,KACN;AACR,WACE,aAAY,SAAS,IAAI,aAAa,IACtC,cACA,aAAY,SAAS,IAAI,eAAe,WAAW,IACnD,aAAY,GAAG,EAAE;AAAA,EAErB;AAAA;AAAA,EAGA,OAAc,IAAI,KAAK,oBAAI,KAAK,GAAG;AACjC,WAAO,GAAG,YAAY;AAAA,EACxB;AAAA;AAAA,EAGA,WAAkB,MAAM;AACtB,WAAO,aAAY,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,WAAkB,SAAS;AACzB,WAAO,aAAY,IAAI;AAAA,EACzB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/dateTimeStr.ts"],"sourcesContent":["export function yyyyMmDd(dt = new Date(), separator = \"-\"): string {\n const yr = dt.getFullYear();\n const mth = dt.getMonth() + 1;\n const day = dt.getDate();\n\n return (\n yr +\n separator +\n (mth < 10 ? \"0\" + mth : mth) +\n separator +\n (day < 10 ? \"0\" + day : day)\n );\n}\n\nexport function hhMmSs(dt = new Date(), separator = \":\"): string {\n const hr = dt.getHours();\n const min = dt.getMinutes();\n const sec = dt.getSeconds();\n\n return (\n (hr < 10 ? \"0\" + hr : hr) +\n separator +\n (min < 10 ? \"0\" + min : min) +\n separator +\n (sec < 10 ? \"0\" + sec : sec)\n );\n}\n\nexport function hhMmSsMs(\n dt = new Date(),\n timeSeparator = \":\",\n msSeparator = \".\",\n): string {\n const ms = dt.getMilliseconds();\n\n return (\n hhMmSs(dt, timeSeparator) +\n msSeparator +\n (ms < 10 ? \"00\" + ms : ms < 100 ? \"0\" + ms : ms)\n );\n}\n\nexport function tzShort(dt = new Date()): string {\n if (dt.getTimezoneOffset() === 0) {\n return \"Z\";\n }\n\n const tzHours = dt.getTimezoneOffset() / 60;\n return tzHours >= 0 ? \"+\" + tzHours : String(tzHours);\n}\n\nexport function getLongMonthNameZeroIndexed(\n month: number,\n locales: Intl.LocalesArgument = \"default\",\n): string {\n return new Date(2024, month, 15).toLocaleString(locales, {\n month: \"long\",\n });\n}\n\nexport function getLongMonthNameOneIndexed(\n month: number,\n locales: Intl.LocalesArgument = \"default\",\n): string {\n return getLongMonthNameZeroIndexed(month - 1, locales);\n}\n\nexport function getShortMonthNameZeroIndexed(\n month: number,\n locales: Intl.LocalesArgument = \"default\",\n): string {\n return new Date(2000, month, 15).toLocaleString(locales, {\n month: \"short\",\n });\n}\n\nexport function getShortMonthNameOneIndexed(\n month: number,\n locales: Intl.LocalesArgument = \"default\",\n): string {\n return getShortMonthNameZeroIndexed(month - 1, locales);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,SAAS,KAAK,oBAAI,KAAK,GAAG,YAAY,KAAa;AACjE,QAAM,KAAK,GAAG,YAAY;AAC1B,QAAM,MAAM,GAAG,SAAS,IAAI;AAC5B,QAAM,MAAM,GAAG,QAAQ;AAEvB,SACE,KACA,aACC,MAAM,KAAK,MAAM,MAAM,OACxB,aACC,MAAM,KAAK,MAAM,MAAM;AAE5B;AAEO,SAAS,OAAO,KAAK,oBAAI,KAAK,GAAG,YAAY,KAAa;AAC/D,QAAM,KAAK,GAAG,SAAS;AACvB,QAAM,MAAM,GAAG,WAAW;AAC1B,QAAM,MAAM,GAAG,WAAW;AAE1B,UACG,KAAK,KAAK,MAAM,KAAK,MACtB,aACC,MAAM,KAAK,MAAM,MAAM,OACxB,aACC,MAAM,KAAK,MAAM,MAAM;AAE5B;AAEO,SAAS,SACd,KAAK,oBAAI,KAAK,GACd,gBAAgB,KAChB,cAAc,KACN;AACR,QAAM,KAAK,GAAG,gBAAgB;AAE9B,SACE,OAAO,IAAI,aAAa,IACxB,eACC,KAAK,KAAK,OAAO,KAAK,KAAK,MAAM,MAAM,KAAK;AAEjD;AAEO,SAAS,QAAQ,KAAK,oBAAI,KAAK,GAAW;AAC/C,MAAI,GAAG,kBAAkB,MAAM,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,GAAG,kBAAkB,IAAI;AACzC,SAAO,WAAW,IAAI,MAAM,UAAU,OAAO,OAAO;AACtD;AAEO,SAAS,4BACd,OACA,UAAgC,WACxB;AACR,SAAO,IAAI,KAAK,MAAM,OAAO,EAAE,EAAE,eAAe,SAAS;AAAA,IACvD,OAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,2BACd,OACA,UAAgC,WACxB;AACR,SAAO,4BAA4B,QAAQ,GAAG,OAAO;AACvD;AAEO,SAAS,6BACd,OACA,UAAgC,WACxB;AACR,SAAO,IAAI,KAAK,KAAM,OAAO,EAAE,EAAE,eAAe,SAAS;AAAA,IACvD,OAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,4BACd,OACA,UAAgC,WACxB;AACR,SAAO,6BAA6B,QAAQ,GAAG,OAAO;AACxD;","names":[]}
package/duration.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Bunch of miscellaneous constants and utility functions related to handling
3
+ * date and time durations.
4
+ *
5
+ * Note that month and year do not have fixed durations, and hence are excluded
6
+ * from this file.
7
+ */
8
+ export type Duration = {
9
+ days?: number;
10
+ hours?: number;
11
+ minutes?: number;
12
+ seconds?: number;
13
+ milliseconds?: number;
14
+ };
15
+ export type DurationType = keyof Duration;
16
+ export declare const DURATION_TYPE_SEQUENCE: DurationType[];
17
+ /**
18
+ * Follows the same format as Intl.DurationFormat.prototype.format().
19
+ *
20
+ * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns
21
+ * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,
22
+ * 7 milliseconds, 8 microseconds, 9 nanoseconds
23
+ * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns
24
+ */
25
+ export type DurationStyle = "short" | "long" | "narrow";
26
+ export type DurationSuffixMap = {
27
+ short: string;
28
+ shorts: string;
29
+ long: string;
30
+ longs: string;
31
+ narrow: string;
32
+ };
33
+ export type DurationSuffixType = keyof DurationSuffixMap;
34
+ export declare const DURATION_STYLE_SUFFIX_MAP: Record<DurationType, DurationSuffixMap>;
35
+ /**
36
+ * Convert a milliseconds duration into a Duration object. If the given ms is
37
+ * zero, then return an object with a single field of zero.
38
+ *
39
+ * durationTypeForZero - Defaults to 'milliseconds'
40
+ */
41
+ export declare function msToDuration(ms: number, durationTypeForZero?: DurationType): Duration;
42
+ /**
43
+ * Returns the number of milliseconds for the given duration.
44
+ */
45
+ export declare function durationToMs(duration: Duration): number;
46
+ /**
47
+ * Format a given Duration object into a string. If the object has no fields,
48
+ * then returns an empty string.
49
+ *
50
+ * style - Defaults to 'short'
51
+ */
52
+ export declare function formatDuration(duration: Duration, style?: DurationStyle): string;
53
+ /**
54
+ * Convert a millisecond duration into a human-readable duration string.
55
+ *
56
+ * options.durationTypeForZero - Defaults to 'milliseconds'
57
+ * options.style - Defaults to 'short'
58
+ */
59
+ export declare function readableDuration(ms: number, options?: {
60
+ durationTypeForZero?: DurationType;
61
+ style?: DurationStyle;
62
+ }): string;
package/duration.js ADDED
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/duration.ts
21
+ var duration_exports = {};
22
+ __export(duration_exports, {
23
+ DURATION_STYLE_SUFFIX_MAP: () => DURATION_STYLE_SUFFIX_MAP,
24
+ DURATION_TYPE_SEQUENCE: () => DURATION_TYPE_SEQUENCE,
25
+ durationToMs: () => durationToMs,
26
+ formatDuration: () => formatDuration,
27
+ msToDuration: () => msToDuration,
28
+ readableDuration: () => readableDuration
29
+ });
30
+ module.exports = __toCommonJS(duration_exports);
31
+
32
+ // src/timeConstants.ts
33
+ var MS_PER_SECOND = 1e3;
34
+ var MS_PER_MINUTE = 6e4;
35
+ var MS_PER_HOUR = 36e5;
36
+ var MS_PER_DAY = 864e5;
37
+ var SECONDS_PER_MINUTE = 60;
38
+ var MINUTES_PER_HOUR = 60;
39
+ var HOURS_PER_DAY = 24;
40
+
41
+ // src/duration.ts
42
+ var DURATION_TYPE_SEQUENCE = [
43
+ "days",
44
+ "hours",
45
+ "minutes",
46
+ "seconds",
47
+ "milliseconds"
48
+ ];
49
+ var DURATION_STYLE_SUFFIX_MAP = {
50
+ days: {
51
+ short: "day",
52
+ shorts: "days",
53
+ long: "day",
54
+ longs: "days",
55
+ narrow: "d"
56
+ },
57
+ hours: {
58
+ short: "hr",
59
+ shorts: "hrs",
60
+ long: "hour",
61
+ longs: "hours",
62
+ narrow: "h"
63
+ },
64
+ minutes: {
65
+ short: "min",
66
+ shorts: "mins",
67
+ long: "minute",
68
+ longs: "minutes",
69
+ narrow: "m"
70
+ },
71
+ seconds: {
72
+ short: "sec",
73
+ shorts: "secs",
74
+ long: "second",
75
+ longs: "seconds",
76
+ narrow: "s"
77
+ },
78
+ milliseconds: {
79
+ short: "ms",
80
+ shorts: "ms",
81
+ long: "millisecond",
82
+ longs: "milliseconds",
83
+ narrow: "ms"
84
+ }
85
+ };
86
+ function getDurationStyleForPlural(style) {
87
+ return style == "short" ? "shorts" : style === "long" ? "longs" : style;
88
+ }
89
+ function getValueAndUnitSeparator(style) {
90
+ return style === "narrow" ? "" : " ";
91
+ }
92
+ function getDurationTypeSeparator(style) {
93
+ return style === "narrow" ? " " : ", ";
94
+ }
95
+ function msToDuration(ms, durationTypeForZero) {
96
+ if (ms === 0) {
97
+ durationTypeForZero = durationTypeForZero != null ? durationTypeForZero : "milliseconds";
98
+ return { [durationTypeForZero]: 0 };
99
+ }
100
+ const duration = {};
101
+ for (let i = 0; i < 1; i++) {
102
+ let seconds = Math.floor(ms / MS_PER_SECOND);
103
+ const millis = ms - seconds * MS_PER_SECOND;
104
+ if (millis > 0) {
105
+ duration["milliseconds"] = millis;
106
+ }
107
+ if (seconds === 0) {
108
+ break;
109
+ }
110
+ let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
111
+ seconds -= minutes * SECONDS_PER_MINUTE;
112
+ if (seconds > 0) {
113
+ duration["seconds"] = seconds;
114
+ }
115
+ if (minutes === 0) {
116
+ break;
117
+ }
118
+ let hours = Math.floor(minutes / MINUTES_PER_HOUR);
119
+ minutes -= hours * MINUTES_PER_HOUR;
120
+ if (minutes > 0) {
121
+ duration["minutes"] = minutes;
122
+ }
123
+ if (hours === 0) {
124
+ break;
125
+ }
126
+ const days = Math.floor(hours / HOURS_PER_DAY);
127
+ hours -= days * HOURS_PER_DAY;
128
+ if (hours > 0) {
129
+ duration["hours"] = hours;
130
+ }
131
+ if (days > 0) {
132
+ duration["days"] = days;
133
+ }
134
+ }
135
+ return duration;
136
+ }
137
+ function durationToMs(duration) {
138
+ var _a, _b, _c, _d, _e;
139
+ const daysMs = ((_a = duration.days) != null ? _a : 0) * MS_PER_DAY;
140
+ const hoursMs = ((_b = duration.hours) != null ? _b : 0) * MS_PER_HOUR;
141
+ const minsMs = ((_c = duration.minutes) != null ? _c : 0) * MS_PER_MINUTE;
142
+ const secsMs = ((_d = duration.seconds) != null ? _d : 0) * MS_PER_SECOND;
143
+ const msMs = (_e = duration.milliseconds) != null ? _e : 0;
144
+ return daysMs + hoursMs + minsMs + secsMs + msMs;
145
+ }
146
+ function formatDuration(duration, style) {
147
+ style = style != null ? style : "short";
148
+ const stylePlural = getDurationStyleForPlural(style);
149
+ const space = getValueAndUnitSeparator(style);
150
+ const a = [];
151
+ for (const unit of DURATION_TYPE_SEQUENCE) {
152
+ const value = duration[unit];
153
+ if (value === void 0) continue;
154
+ const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];
155
+ const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];
156
+ a.push(value + space + suffix);
157
+ }
158
+ const separator = getDurationTypeSeparator(style);
159
+ return a.join(separator);
160
+ }
161
+ function readableDuration(ms, options) {
162
+ const duration = msToDuration(ms, options == null ? void 0 : options.durationTypeForZero);
163
+ return formatDuration(duration, options == null ? void 0 : options.style);
164
+ }
165
+ // Annotate the CommonJS export names for ESM import in node:
166
+ 0 && (module.exports = {
167
+ DURATION_STYLE_SUFFIX_MAP,
168
+ DURATION_TYPE_SEQUENCE,
169
+ durationToMs,
170
+ formatDuration,
171
+ msToDuration,
172
+ readableDuration
173
+ });
174
+ //# sourceMappingURL=duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/duration.ts","../src/timeConstants.ts"],"sourcesContent":["/**\n * Bunch of miscellaneous constants and utility functions related to handling\n * date and time durations.\n *\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nimport {\n MS_PER_SECOND,\n SECONDS_PER_MINUTE,\n MINUTES_PER_HOUR,\n HOURS_PER_DAY,\n MS_PER_DAY,\n MS_PER_MINUTE,\n MS_PER_HOUR,\n} from \"./timeConstants\";\n\nexport type Duration = {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n};\n\nexport type DurationType = keyof Duration;\n\nexport const DURATION_TYPE_SEQUENCE: DurationType[] = [\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n];\n\n/**\n * Follows the same format as Intl.DurationFormat.prototype.format().\n *\n * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns\n * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,\n * 7 milliseconds, 8 microseconds, 9 nanoseconds\n * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns\n */\nexport type DurationStyle = \"short\" | \"long\" | \"narrow\";\n\nexport type DurationSuffixMap = {\n short: string;\n shorts: string;\n long: string;\n longs: string;\n narrow: string;\n};\n\nexport type DurationSuffixType = keyof DurationSuffixMap;\n\nexport const DURATION_STYLE_SUFFIX_MAP: Record<\n DurationType,\n DurationSuffixMap\n> = {\n days: {\n short: \"day\",\n shorts: \"days\",\n long: \"day\",\n longs: \"days\",\n narrow: \"d\",\n },\n hours: {\n short: \"hr\",\n shorts: \"hrs\",\n long: \"hour\",\n longs: \"hours\",\n narrow: \"h\",\n },\n minutes: {\n short: \"min\",\n shorts: \"mins\",\n long: \"minute\",\n longs: \"minutes\",\n narrow: \"m\",\n },\n seconds: {\n short: \"sec\",\n shorts: \"secs\",\n long: \"second\",\n longs: \"seconds\",\n narrow: \"s\",\n },\n milliseconds: {\n short: \"ms\",\n shorts: \"ms\",\n long: \"millisecond\",\n longs: \"milliseconds\",\n narrow: \"ms\",\n },\n};\n\nfunction getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {\n return style == \"short\" ? \"shorts\" : style === \"long\" ? \"longs\" : style;\n}\n\nfunction getValueAndUnitSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \"\" : \" \";\n}\n\nfunction getDurationTypeSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \" \" : \", \";\n}\n\n/**\n * Convert a milliseconds duration into a Duration object. If the given ms is\n * zero, then return an object with a single field of zero.\n *\n * durationTypeForZero - Defaults to 'milliseconds'\n */\nexport function msToDuration(\n ms: number,\n durationTypeForZero?: DurationType,\n): Duration {\n if (ms === 0) {\n durationTypeForZero = durationTypeForZero ?? \"milliseconds\";\n return { [durationTypeForZero]: 0 };\n }\n\n const duration: Duration = {};\n\n for (let i = 0; i < 1; i++) {\n let seconds = Math.floor(ms / MS_PER_SECOND);\n const millis = ms - seconds * MS_PER_SECOND;\n\n if (millis > 0) {\n duration[\"milliseconds\"] = millis;\n }\n\n if (seconds === 0) {\n break;\n }\n\n let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);\n seconds -= minutes * SECONDS_PER_MINUTE;\n\n if (seconds > 0) {\n duration[\"seconds\"] = seconds;\n }\n\n if (minutes === 0) {\n break;\n }\n\n let hours = Math.floor(minutes / MINUTES_PER_HOUR);\n minutes -= hours * MINUTES_PER_HOUR;\n\n if (minutes > 0) {\n duration[\"minutes\"] = minutes;\n }\n\n if (hours === 0) {\n break;\n }\n\n const days = Math.floor(hours / HOURS_PER_DAY);\n hours -= days * HOURS_PER_DAY;\n\n if (hours > 0) {\n duration[\"hours\"] = hours;\n }\n\n if (days > 0) {\n duration[\"days\"] = days;\n }\n }\n\n return duration;\n}\n\n/**\n * Returns the number of milliseconds for the given duration.\n */\nexport function durationToMs(duration: Duration): number {\n const daysMs = (duration.days ?? 0) * MS_PER_DAY;\n const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;\n const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;\n const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;\n const msMs = duration.milliseconds ?? 0;\n\n return daysMs + hoursMs + minsMs + secsMs + msMs;\n}\n\n/**\n * Format a given Duration object into a string. If the object has no fields,\n * then returns an empty string.\n *\n * style - Defaults to 'short'\n */\nexport function formatDuration(duration: Duration, style?: DurationStyle) {\n style = style ?? \"short\";\n const stylePlural = getDurationStyleForPlural(style);\n\n const space = getValueAndUnitSeparator(style);\n\n const a: string[] = [];\n\n for (const unit of DURATION_TYPE_SEQUENCE) {\n const value = duration[unit];\n if (value === undefined) continue;\n\n const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];\n const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];\n a.push(value + space + suffix);\n }\n\n const separator = getDurationTypeSeparator(style);\n return a.join(separator);\n}\n\n/**\n * Convert a millisecond duration into a human-readable duration string.\n *\n * options.durationTypeForZero - Defaults to 'milliseconds'\n * options.style - Defaults to 'short'\n */\nexport function readableDuration(\n ms: number,\n options?: { durationTypeForZero?: DurationType; style?: DurationStyle },\n): string {\n const duration = msToDuration(ms, options?.durationTypeForZero);\n\n return formatDuration(duration, options?.style);\n}\n","/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,aAAa;AAGnB,IAAM,qBAAqB;AAK3B,IAAM,mBAAmB;AAIzB,IAAM,gBAAgB;;;ADQtB,IAAM,yBAAyC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAsBO,IAAM,4BAGT;AAAA,EACF,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACF;AAEA,SAAS,0BAA0B,OAA0C;AAC3E,SAAO,SAAS,UAAU,WAAW,UAAU,SAAS,UAAU;AACpE;AAEA,SAAS,yBAAyB,OAA8B;AAC9D,SAAO,UAAU,WAAW,KAAK;AACnC;AAEA,SAAS,yBAAyB,OAA8B;AAC9D,SAAO,UAAU,WAAW,MAAM;AACpC;AAQO,SAAS,aACd,IACA,qBACU;AACV,MAAI,OAAO,GAAG;AACZ,0BAAsB,oDAAuB;AAC7C,WAAO,EAAE,CAAC,mBAAmB,GAAG,EAAE;AAAA,EACpC;AAEA,QAAM,WAAqB,CAAC;AAE5B,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,UAAU,KAAK,MAAM,KAAK,aAAa;AAC3C,UAAM,SAAS,KAAK,UAAU;AAE9B,QAAI,SAAS,GAAG;AACd,eAAS,cAAc,IAAI;AAAA,IAC7B;AAEA,QAAI,YAAY,GAAG;AACjB;AAAA,IACF;AAEA,QAAI,UAAU,KAAK,MAAM,UAAU,kBAAkB;AACrD,eAAW,UAAU;AAErB,QAAI,UAAU,GAAG;AACf,eAAS,SAAS,IAAI;AAAA,IACxB;AAEA,QAAI,YAAY,GAAG;AACjB;AAAA,IACF;AAEA,QAAI,QAAQ,KAAK,MAAM,UAAU,gBAAgB;AACjD,eAAW,QAAQ;AAEnB,QAAI,UAAU,GAAG;AACf,eAAS,SAAS,IAAI;AAAA,IACxB;AAEA,QAAI,UAAU,GAAG;AACf;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,MAAM,QAAQ,aAAa;AAC7C,aAAS,OAAO;AAEhB,QAAI,QAAQ,GAAG;AACb,eAAS,OAAO,IAAI;AAAA,IACtB;AAEA,QAAI,OAAO,GAAG;AACZ,eAAS,MAAM,IAAI;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,UAA4B;AAlLzD;AAmLE,QAAM,WAAU,cAAS,SAAT,YAAiB,KAAK;AACtC,QAAM,YAAW,cAAS,UAAT,YAAkB,KAAK;AACxC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,QAAO,cAAS,iBAAT,YAAyB;AAEtC,SAAO,SAAS,UAAU,SAAS,SAAS;AAC9C;AAQO,SAAS,eAAe,UAAoB,OAAuB;AACxE,UAAQ,wBAAS;AACjB,QAAM,cAAc,0BAA0B,KAAK;AAEnD,QAAM,QAAQ,yBAAyB,KAAK;AAE5C,QAAM,IAAc,CAAC;AAErB,aAAW,QAAQ,wBAAwB;AACzC,UAAM,QAAQ,SAAS,IAAI;AAC3B,QAAI,UAAU,OAAW;AAEzB,UAAM,YAAY,0BAA0B,IAAI;AAChD,UAAM,SAAS,UAAU,IAAI,UAAU,KAAK,IAAI,UAAU,WAAW;AACrE,MAAE,KAAK,QAAQ,QAAQ,MAAM;AAAA,EAC/B;AAEA,QAAM,YAAY,yBAAyB,KAAK;AAChD,SAAO,EAAE,KAAK,SAAS;AACzB;AAQO,SAAS,iBACd,IACA,SACQ;AACR,QAAM,WAAW,aAAa,IAAI,mCAAS,mBAAmB;AAE9D,SAAO,eAAe,UAAU,mCAAS,KAAK;AAChD;","names":[]}
@@ -1,18 +1,38 @@
1
+ import { Duration } from "./duration";
1
2
  /**
2
3
  * Simple local storage cache with support for auto-expiration.
3
4
  * Note that this works in the browser context only because nodejs does not
4
5
  * have local storage.
6
+ *
7
+ * Create a cache item accessor object with auto-expiration. The value will
8
+ * always be stored as a string by applying JSON.stringify(), and will be
9
+ * returned in the same object type by applying JSON.parse().
10
+ *
11
+ * In order to provide proper type-checking, please always specify the T
12
+ * type parameter. E.g. const item = storeItem<string>("name", 10_000);
13
+ *
14
+ * expires - Either a number in milliseconds, or a Duration object
5
15
  */
6
- export declare class LocalStorageCache {
7
- static setValue<T>(key: string, value: T, expireDeltaMs: number): void;
8
- static getValue<T>(key: string, logError?: boolean): T | undefined;
9
- }
10
- /** Same as above, but saves some config for reuse. */
11
- export declare class LocalStorageCacheItem<T> {
16
+ export declare function storeItem<T>(key: string, expires: number | Duration, logError?: boolean, defaultValue?: T): CacheItem<T>;
17
+ declare class CacheItem<T> {
12
18
  readonly key: string;
13
19
  readonly expireDeltaMs: number;
14
20
  readonly logError: boolean;
15
- constructor(key: string, expireDeltaMs: number, logError?: boolean, defaultValue?: T);
21
+ /**
22
+ * Create a cache item accessor object with auto-expiration.
23
+ */
24
+ constructor(key: string, expireDeltaMs: number, logError: boolean, defaultValue: T | undefined);
25
+ /**
26
+ * Set the value of this item with auto-expiration.
27
+ */
16
28
  set(value: T): void;
29
+ /**
30
+ * Get the value of this item, or undefined if value is not set or expired.
31
+ */
17
32
  get(): T | undefined;
33
+ /**
34
+ * Remove the value of this item.
35
+ */
36
+ remove(): void;
18
37
  }
38
+ export {};
@@ -20,58 +20,89 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/localStorageCache.ts
21
21
  var localStorageCache_exports = {};
22
22
  __export(localStorageCache_exports, {
23
- LocalStorageCache: () => LocalStorageCache,
24
- LocalStorageCacheItem: () => LocalStorageCacheItem
23
+ storeItem: () => storeItem
25
24
  });
26
25
  module.exports = __toCommonJS(localStorageCache_exports);
27
- var LocalStorageCache = class {
28
- static setValue(key, value, expireDeltaMs) {
29
- const expireMs = Date.now() + expireDeltaMs;
26
+
27
+ // src/timeConstants.ts
28
+ var MS_PER_SECOND = 1e3;
29
+ var MS_PER_MINUTE = 6e4;
30
+ var MS_PER_HOUR = 36e5;
31
+ var MS_PER_DAY = 864e5;
32
+
33
+ // src/duration.ts
34
+ function durationToMs(duration) {
35
+ var _a, _b, _c, _d, _e;
36
+ const daysMs = ((_a = duration.days) != null ? _a : 0) * MS_PER_DAY;
37
+ const hoursMs = ((_b = duration.hours) != null ? _b : 0) * MS_PER_HOUR;
38
+ const minsMs = ((_c = duration.minutes) != null ? _c : 0) * MS_PER_MINUTE;
39
+ const secsMs = ((_d = duration.seconds) != null ? _d : 0) * MS_PER_SECOND;
40
+ const msMs = (_e = duration.milliseconds) != null ? _e : 0;
41
+ return daysMs + hoursMs + minsMs + secsMs + msMs;
42
+ }
43
+
44
+ // src/localStorageCache.ts
45
+ function storeItem(key, expires, logError = true, defaultValue) {
46
+ const expireDeltaMs = typeof expires === "number" ? expires : durationToMs(expires);
47
+ return new CacheItem(key, expireDeltaMs, logError, defaultValue);
48
+ }
49
+ var CacheItem = class {
50
+ /**
51
+ * Create a cache item accessor object with auto-expiration.
52
+ */
53
+ constructor(key, expireDeltaMs, logError, defaultValue) {
54
+ this.key = key;
55
+ this.expireDeltaMs = expireDeltaMs;
56
+ this.logError = logError;
57
+ if (defaultValue !== void 0) {
58
+ if (this.get() === void 0) {
59
+ this.set(defaultValue);
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Set the value of this item with auto-expiration.
65
+ */
66
+ set(value) {
67
+ const expireMs = Date.now() + this.expireDeltaMs;
30
68
  const valueStr = JSON.stringify({ value, expireMs });
31
- globalThis.localStorage.setItem(key, valueStr);
69
+ globalThis.localStorage.setItem(this.key, valueStr);
32
70
  }
33
- static getValue(key, logError = true) {
34
- const jsonStr = globalThis.localStorage.getItem(key);
71
+ /**
72
+ * Get the value of this item, or undefined if value is not set or expired.
73
+ */
74
+ get() {
75
+ const jsonStr = globalThis.localStorage.getItem(this.key);
35
76
  if (!jsonStr || typeof jsonStr !== "string") {
36
77
  return void 0;
37
78
  }
38
79
  try {
39
80
  const obj = JSON.parse(jsonStr);
40
81
  if (!obj || typeof obj !== "object" || !("value" in obj) || !("expireMs" in obj) || typeof obj.expireMs !== "number" || Date.now() >= obj.expireMs) {
41
- globalThis.localStorage.removeItem(key);
82
+ globalThis.localStorage.removeItem(this.key);
42
83
  return void 0;
43
84
  }
44
85
  return obj.value;
45
86
  } catch (e) {
46
- if (logError) {
47
- console.error(`Found invalid storage value: ${key}=${jsonStr}:`, e);
87
+ if (this.logError) {
88
+ console.error(
89
+ `Found invalid storage value: ${this.key}=${jsonStr}:`,
90
+ e
91
+ );
48
92
  }
49
- globalThis.localStorage.removeItem(key);
93
+ globalThis.localStorage.removeItem(this.key);
50
94
  return void 0;
51
95
  }
52
96
  }
53
- };
54
- var LocalStorageCacheItem = class {
55
- constructor(key, expireDeltaMs, logError = true, defaultValue) {
56
- this.key = key;
57
- this.expireDeltaMs = expireDeltaMs;
58
- this.logError = logError;
59
- if (defaultValue !== void 0) {
60
- if (this.get() === void 0) {
61
- this.set(defaultValue);
62
- }
63
- }
64
- }
65
- set(value) {
66
- LocalStorageCache.setValue(this.key, value, this.expireDeltaMs);
67
- }
68
- get() {
69
- return LocalStorageCache.getValue(this.key, this.logError);
97
+ /**
98
+ * Remove the value of this item.
99
+ */
100
+ remove() {
101
+ globalThis.localStorage.removeItem(this.key);
70
102
  }
71
103
  };
72
104
  // Annotate the CommonJS export names for ESM import in node:
73
105
  0 && (module.exports = {
74
- LocalStorageCache,
75
- LocalStorageCacheItem
106
+ storeItem
76
107
  });
77
108
  //# sourceMappingURL=localStorageCache.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/localStorageCache.ts"],"sourcesContent":["/**\n * Simple local storage cache with support for auto-expiration.\n * Note that this works in the browser context only because nodejs does not\n * have local storage.\n */\nexport class LocalStorageCache {\n public static setValue<T>(key: string, value: T, expireDeltaMs: number) {\n const expireMs = Date.now() + expireDeltaMs;\n const valueStr = JSON.stringify({ value, expireMs });\n\n globalThis.localStorage.setItem(key, valueStr);\n }\n\n public static getValue<T>(key: string, logError = true): T | undefined {\n const jsonStr = globalThis.localStorage.getItem(key);\n\n if (!jsonStr || typeof jsonStr !== \"string\") {\n return undefined;\n }\n\n try {\n const obj: { value: T; expireMs: number } | undefined =\n JSON.parse(jsonStr);\n if (\n !obj ||\n typeof obj !== \"object\" ||\n !(\"value\" in obj) ||\n !(\"expireMs\" in obj) ||\n typeof obj.expireMs !== \"number\" ||\n Date.now() >= obj.expireMs\n ) {\n globalThis.localStorage.removeItem(key);\n return undefined;\n }\n\n return obj.value;\n } catch (e) {\n if (logError) {\n console.error(`Found invalid storage value: ${key}=${jsonStr}:`, e);\n }\n globalThis.localStorage.removeItem(key);\n return undefined;\n }\n }\n}\n\n/** Same as above, but saves some config for reuse. */\nexport class LocalStorageCacheItem<T> {\n public constructor(\n public readonly key: string,\n public readonly expireDeltaMs: number,\n public readonly logError = true,\n defaultValue?: T,\n ) {\n if (defaultValue !== undefined) {\n if (this.get() === undefined) {\n this.set(defaultValue);\n }\n }\n }\n\n public set(value: T) {\n LocalStorageCache.setValue(this.key, value, this.expireDeltaMs);\n }\n\n public get(): T | undefined {\n return LocalStorageCache.getValue(this.key, this.logError);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,IAAM,oBAAN,MAAwB;AAAA,EAC7B,OAAc,SAAY,KAAa,OAAU,eAAuB;AACtE,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,UAAM,WAAW,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAEnD,eAAW,aAAa,QAAQ,KAAK,QAAQ;AAAA,EAC/C;AAAA,EAEA,OAAc,SAAY,KAAa,WAAW,MAAqB;AACrE,UAAM,UAAU,WAAW,aAAa,QAAQ,GAAG;AAEnD,QAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,MACJ,KAAK,MAAM,OAAO;AACpB,UACE,CAAC,OACD,OAAO,QAAQ,YACf,EAAE,WAAW,QACb,EAAE,cAAc,QAChB,OAAO,IAAI,aAAa,YACxB,KAAK,IAAI,KAAK,IAAI,UAClB;AACA,mBAAW,aAAa,WAAW,GAAG;AACtC,eAAO;AAAA,MACT;AAEA,aAAO,IAAI;AAAA,IACb,SAAS,GAAG;AACV,UAAI,UAAU;AACZ,gBAAQ,MAAM,gCAAgC,GAAG,IAAI,OAAO,KAAK,CAAC;AAAA,MACpE;AACA,iBAAW,aAAa,WAAW,GAAG;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAGO,IAAM,wBAAN,MAA+B;AAAA,EAC7B,YACW,KACA,eACA,WAAW,MAC3B,cACA;AAJgB;AACA;AACA;AAGhB,QAAI,iBAAiB,QAAW;AAC9B,UAAI,KAAK,IAAI,MAAM,QAAW;AAC5B,aAAK,IAAI,YAAY;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEO,IAAI,OAAU;AACnB,sBAAkB,SAAS,KAAK,KAAK,OAAO,KAAK,aAAa;AAAA,EAChE;AAAA,EAEO,MAAqB;AAC1B,WAAO,kBAAkB,SAAS,KAAK,KAAK,KAAK,QAAQ;AAAA,EAC3D;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/localStorageCache.ts","../src/timeConstants.ts","../src/duration.ts"],"sourcesContent":["import { Duration, durationToMs } from \"./duration\";\n\n/**\n * Simple local storage cache with support for auto-expiration.\n * Note that this works in the browser context only because nodejs does not\n * have local storage.\n *\n * Create a cache item accessor object with auto-expiration. The value will\n * always be stored as a string by applying JSON.stringify(), and will be\n * returned in the same object type by applying JSON.parse().\n *\n * In order to provide proper type-checking, please always specify the T\n * type parameter. E.g. const item = storeItem<string>(\"name\", 10_000);\n *\n * expires - Either a number in milliseconds, or a Duration object\n */\nexport function storeItem<T>(\n key: string,\n expires: number | Duration,\n logError = true,\n defaultValue?: T,\n) {\n const expireDeltaMs =\n typeof expires === \"number\" ? expires : durationToMs(expires);\n\n return new CacheItem<T>(key, expireDeltaMs, logError, defaultValue);\n}\n\nclass CacheItem<T> {\n /**\n * Create a cache item accessor object with auto-expiration.\n */\n public constructor(\n public readonly key: string,\n public readonly expireDeltaMs: number,\n public readonly logError: boolean,\n defaultValue: T | undefined,\n ) {\n if (defaultValue !== undefined) {\n if (this.get() === undefined) {\n this.set(defaultValue);\n }\n }\n }\n\n /**\n * Set the value of this item with auto-expiration.\n */\n public set(value: T): void {\n const expireMs = Date.now() + this.expireDeltaMs;\n const valueStr = JSON.stringify({ value, expireMs });\n\n globalThis.localStorage.setItem(this.key, valueStr);\n }\n\n /**\n * Get the value of this item, or undefined if value is not set or expired.\n */\n public get(): T | undefined {\n const jsonStr = globalThis.localStorage.getItem(this.key);\n\n if (!jsonStr || typeof jsonStr !== \"string\") {\n return undefined;\n }\n\n try {\n const obj: { value: T; expireMs: number } | undefined =\n JSON.parse(jsonStr);\n if (\n !obj ||\n typeof obj !== \"object\" ||\n !(\"value\" in obj) ||\n !(\"expireMs\" in obj) ||\n typeof obj.expireMs !== \"number\" ||\n Date.now() >= obj.expireMs\n ) {\n globalThis.localStorage.removeItem(this.key);\n return undefined;\n }\n\n return obj.value;\n } catch (e) {\n if (this.logError) {\n console.error(\n `Found invalid storage value: ${this.key}=${jsonStr}:`,\n e,\n );\n }\n globalThis.localStorage.removeItem(this.key);\n return undefined;\n }\n }\n\n /**\n * Remove the value of this item.\n */\n public remove(): void {\n globalThis.localStorage.removeItem(this.key);\n }\n}\n","/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n","/**\n * Bunch of miscellaneous constants and utility functions related to handling\n * date and time durations.\n *\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nimport {\n MS_PER_SECOND,\n SECONDS_PER_MINUTE,\n MINUTES_PER_HOUR,\n HOURS_PER_DAY,\n MS_PER_DAY,\n MS_PER_MINUTE,\n MS_PER_HOUR,\n} from \"./timeConstants\";\n\nexport type Duration = {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n};\n\nexport type DurationType = keyof Duration;\n\nexport const DURATION_TYPE_SEQUENCE: DurationType[] = [\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n];\n\n/**\n * Follows the same format as Intl.DurationFormat.prototype.format().\n *\n * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns\n * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,\n * 7 milliseconds, 8 microseconds, 9 nanoseconds\n * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns\n */\nexport type DurationStyle = \"short\" | \"long\" | \"narrow\";\n\nexport type DurationSuffixMap = {\n short: string;\n shorts: string;\n long: string;\n longs: string;\n narrow: string;\n};\n\nexport type DurationSuffixType = keyof DurationSuffixMap;\n\nexport const DURATION_STYLE_SUFFIX_MAP: Record<\n DurationType,\n DurationSuffixMap\n> = {\n days: {\n short: \"day\",\n shorts: \"days\",\n long: \"day\",\n longs: \"days\",\n narrow: \"d\",\n },\n hours: {\n short: \"hr\",\n shorts: \"hrs\",\n long: \"hour\",\n longs: \"hours\",\n narrow: \"h\",\n },\n minutes: {\n short: \"min\",\n shorts: \"mins\",\n long: \"minute\",\n longs: \"minutes\",\n narrow: \"m\",\n },\n seconds: {\n short: \"sec\",\n shorts: \"secs\",\n long: \"second\",\n longs: \"seconds\",\n narrow: \"s\",\n },\n milliseconds: {\n short: \"ms\",\n shorts: \"ms\",\n long: \"millisecond\",\n longs: \"milliseconds\",\n narrow: \"ms\",\n },\n};\n\nfunction getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {\n return style == \"short\" ? \"shorts\" : style === \"long\" ? \"longs\" : style;\n}\n\nfunction getValueAndUnitSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \"\" : \" \";\n}\n\nfunction getDurationTypeSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \" \" : \", \";\n}\n\n/**\n * Convert a milliseconds duration into a Duration object. If the given ms is\n * zero, then return an object with a single field of zero.\n *\n * durationTypeForZero - Defaults to 'milliseconds'\n */\nexport function msToDuration(\n ms: number,\n durationTypeForZero?: DurationType,\n): Duration {\n if (ms === 0) {\n durationTypeForZero = durationTypeForZero ?? \"milliseconds\";\n return { [durationTypeForZero]: 0 };\n }\n\n const duration: Duration = {};\n\n for (let i = 0; i < 1; i++) {\n let seconds = Math.floor(ms / MS_PER_SECOND);\n const millis = ms - seconds * MS_PER_SECOND;\n\n if (millis > 0) {\n duration[\"milliseconds\"] = millis;\n }\n\n if (seconds === 0) {\n break;\n }\n\n let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);\n seconds -= minutes * SECONDS_PER_MINUTE;\n\n if (seconds > 0) {\n duration[\"seconds\"] = seconds;\n }\n\n if (minutes === 0) {\n break;\n }\n\n let hours = Math.floor(minutes / MINUTES_PER_HOUR);\n minutes -= hours * MINUTES_PER_HOUR;\n\n if (minutes > 0) {\n duration[\"minutes\"] = minutes;\n }\n\n if (hours === 0) {\n break;\n }\n\n const days = Math.floor(hours / HOURS_PER_DAY);\n hours -= days * HOURS_PER_DAY;\n\n if (hours > 0) {\n duration[\"hours\"] = hours;\n }\n\n if (days > 0) {\n duration[\"days\"] = days;\n }\n }\n\n return duration;\n}\n\n/**\n * Returns the number of milliseconds for the given duration.\n */\nexport function durationToMs(duration: Duration): number {\n const daysMs = (duration.days ?? 0) * MS_PER_DAY;\n const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;\n const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;\n const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;\n const msMs = duration.milliseconds ?? 0;\n\n return daysMs + hoursMs + minsMs + secsMs + msMs;\n}\n\n/**\n * Format a given Duration object into a string. If the object has no fields,\n * then returns an empty string.\n *\n * style - Defaults to 'short'\n */\nexport function formatDuration(duration: Duration, style?: DurationStyle) {\n style = style ?? \"short\";\n const stylePlural = getDurationStyleForPlural(style);\n\n const space = getValueAndUnitSeparator(style);\n\n const a: string[] = [];\n\n for (const unit of DURATION_TYPE_SEQUENCE) {\n const value = duration[unit];\n if (value === undefined) continue;\n\n const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];\n const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];\n a.push(value + space + suffix);\n }\n\n const separator = getDurationTypeSeparator(style);\n return a.join(separator);\n}\n\n/**\n * Convert a millisecond duration into a human-readable duration string.\n *\n * options.durationTypeForZero - Defaults to 'milliseconds'\n * options.style - Defaults to 'short'\n */\nexport function readableDuration(\n ms: number,\n options?: { durationTypeForZero?: DurationType; style?: DurationStyle },\n): string {\n const duration = msToDuration(ms, options?.durationTypeForZero);\n\n return formatDuration(duration, options?.style);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,aAAa;;;AC0KnB,SAAS,aAAa,UAA4B;AAlLzD;AAmLE,QAAM,WAAU,cAAS,SAAT,YAAiB,KAAK;AACtC,QAAM,YAAW,cAAS,UAAT,YAAkB,KAAK;AACxC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,QAAO,cAAS,iBAAT,YAAyB;AAEtC,SAAO,SAAS,UAAU,SAAS,SAAS;AAC9C;;;AF1KO,SAAS,UACd,KACA,SACA,WAAW,MACX,cACA;AACA,QAAM,gBACJ,OAAO,YAAY,WAAW,UAAU,aAAa,OAAO;AAE9D,SAAO,IAAI,UAAa,KAAK,eAAe,UAAU,YAAY;AACpE;AAEA,IAAM,YAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIV,YACW,KACA,eACA,UAChB,cACA;AAJgB;AACA;AACA;AAGhB,QAAI,iBAAiB,QAAW;AAC9B,UAAI,KAAK,IAAI,MAAM,QAAW;AAC5B,aAAK,IAAI,YAAY;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,OAAgB;AACzB,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,UAAM,WAAW,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAEnD,eAAW,aAAa,QAAQ,KAAK,KAAK,QAAQ;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKO,MAAqB;AAC1B,UAAM,UAAU,WAAW,aAAa,QAAQ,KAAK,GAAG;AAExD,QAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,MACJ,KAAK,MAAM,OAAO;AACpB,UACE,CAAC,OACD,OAAO,QAAQ,YACf,EAAE,WAAW,QACb,EAAE,cAAc,QAChB,OAAO,IAAI,aAAa,YACxB,KAAK,IAAI,KAAK,IAAI,UAClB;AACA,mBAAW,aAAa,WAAW,KAAK,GAAG;AAC3C,eAAO;AAAA,MACT;AAEA,aAAO,IAAI;AAAA,IACb,SAAS,GAAG;AACV,UAAI,KAAK,UAAU;AACjB,gBAAQ;AAAA,UACN,gCAAgC,KAAK,GAAG,IAAI,OAAO;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AACA,iBAAW,aAAa,WAAW,KAAK,GAAG;AAC3C,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,SAAe;AACpB,eAAW,aAAa,WAAW,KAAK,GAAG;AAAA,EAC7C;AACF;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@choksheak/ts-utils",
3
3
  "license": "The Unlicense",
4
- "version": "0.1.8",
4
+ "version": "0.1.9",
5
5
  "description": "Random Typescript utilities with support for full tree-shaking",
6
6
  "private": false,
7
7
  "scripts": {
@@ -1,83 +1,82 @@
1
- export class DateTimeStr {
2
- public static yyyyMmDd(dt = new Date(), separator = "-"): string {
3
- const yr = dt.getFullYear();
4
- const mth = dt.getMonth() + 1;
5
- const day = dt.getDate();
1
+ export function yyyyMmDd(dt = new Date(), separator = "-"): string {
2
+ const yr = dt.getFullYear();
3
+ const mth = dt.getMonth() + 1;
4
+ const day = dt.getDate();
6
5
 
7
- return (
8
- yr +
9
- separator +
10
- (mth < 10 ? "0" + mth : mth) +
11
- separator +
12
- (day < 10 ? "0" + day : day)
13
- );
14
- }
15
-
16
- public static hhMmSs(dt = new Date(), separator = ":"): string {
17
- const hr = dt.getHours();
18
- const min = dt.getMinutes();
19
- const sec = dt.getSeconds();
6
+ return (
7
+ yr +
8
+ separator +
9
+ (mth < 10 ? "0" + mth : mth) +
10
+ separator +
11
+ (day < 10 ? "0" + day : day)
12
+ );
13
+ }
20
14
 
21
- return (
22
- (hr < 10 ? "0" + hr : hr) +
23
- separator +
24
- (min < 10 ? "0" + min : min) +
25
- separator +
26
- (sec < 10 ? "0" + sec : sec)
27
- );
28
- }
15
+ export function hhMmSs(dt = new Date(), separator = ":"): string {
16
+ const hr = dt.getHours();
17
+ const min = dt.getMinutes();
18
+ const sec = dt.getSeconds();
29
19
 
30
- public static hhMmSsMs(
31
- dt = new Date(),
32
- timeSeparator = ":",
33
- msSeparator = ".",
34
- ): string {
35
- const ms = dt.getMilliseconds();
20
+ return (
21
+ (hr < 10 ? "0" + hr : hr) +
22
+ separator +
23
+ (min < 10 ? "0" + min : min) +
24
+ separator +
25
+ (sec < 10 ? "0" + sec : sec)
26
+ );
27
+ }
36
28
 
37
- return (
38
- DateTimeStr.hhMmSs(dt, timeSeparator) +
39
- msSeparator +
40
- (ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms)
41
- );
42
- }
29
+ export function hhMmSsMs(
30
+ dt = new Date(),
31
+ timeSeparator = ":",
32
+ msSeparator = ".",
33
+ ): string {
34
+ const ms = dt.getMilliseconds();
43
35
 
44
- public static tz(dt = new Date()): string {
45
- if (dt.getTimezoneOffset() === 0) {
46
- return "Z";
47
- }
36
+ return (
37
+ hhMmSs(dt, timeSeparator) +
38
+ msSeparator +
39
+ (ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms)
40
+ );
41
+ }
48
42
 
49
- const tzHours = dt.getTimezoneOffset() / 60;
50
- return tzHours >= 0 ? "+" + tzHours : String(tzHours);
43
+ export function tzShort(dt = new Date()): string {
44
+ if (dt.getTimezoneOffset() === 0) {
45
+ return "Z";
51
46
  }
52
47
 
53
- /** Full local date/time string. */
54
- public static local(
55
- dt = new Date(),
56
- dateSeparator = "-",
57
- dtSeparator = " ",
58
- timeSeparator = ":",
59
- msSeparator = ".",
60
- ): string {
61
- return (
62
- DateTimeStr.yyyyMmDd(dt, dateSeparator) +
63
- dtSeparator +
64
- DateTimeStr.hhMmSsMs(dt, timeSeparator, msSeparator) +
65
- DateTimeStr.tz(dt)
66
- );
67
- }
48
+ const tzHours = dt.getTimezoneOffset() / 60;
49
+ return tzHours >= 0 ? "+" + tzHours : String(tzHours);
50
+ }
68
51
 
69
- /** Use the default ISO string function to keep things simple here. */
70
- public static utc(dt = new Date()) {
71
- return dt.toISOString();
72
- }
52
+ export function getLongMonthNameZeroIndexed(
53
+ month: number,
54
+ locales: Intl.LocalesArgument = "default",
55
+ ): string {
56
+ return new Date(2024, month, 15).toLocaleString(locales, {
57
+ month: "long",
58
+ });
59
+ }
73
60
 
74
- /** Default full local date+time string with full & concise info. */
75
- public static get now() {
76
- return DateTimeStr.local();
77
- }
61
+ export function getLongMonthNameOneIndexed(
62
+ month: number,
63
+ locales: Intl.LocalesArgument = "default",
64
+ ): string {
65
+ return getLongMonthNameZeroIndexed(month - 1, locales);
66
+ }
78
67
 
79
- /** Default full UTC date+time string with full & concise info. */
80
- public static get utcNow() {
81
- return DateTimeStr.utc();
82
- }
68
+ export function getShortMonthNameZeroIndexed(
69
+ month: number,
70
+ locales: Intl.LocalesArgument = "default",
71
+ ): string {
72
+ return new Date(2000, month, 15).toLocaleString(locales, {
73
+ month: "short",
74
+ });
75
+ }
76
+
77
+ export function getShortMonthNameOneIndexed(
78
+ month: number,
79
+ locales: Intl.LocalesArgument = "default",
80
+ ): string {
81
+ return getShortMonthNameZeroIndexed(month - 1, locales);
83
82
  }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Bunch of miscellaneous constants and utility functions related to handling
3
+ * date and time durations.
4
+ *
5
+ * Note that month and year do not have fixed durations, and hence are excluded
6
+ * from this file.
7
+ */
8
+
9
+ import {
10
+ MS_PER_SECOND,
11
+ SECONDS_PER_MINUTE,
12
+ MINUTES_PER_HOUR,
13
+ HOURS_PER_DAY,
14
+ MS_PER_DAY,
15
+ MS_PER_MINUTE,
16
+ MS_PER_HOUR,
17
+ } from "./timeConstants";
18
+
19
+ export type Duration = {
20
+ days?: number;
21
+ hours?: number;
22
+ minutes?: number;
23
+ seconds?: number;
24
+ milliseconds?: number;
25
+ };
26
+
27
+ export type DurationType = keyof Duration;
28
+
29
+ export const DURATION_TYPE_SEQUENCE: DurationType[] = [
30
+ "days",
31
+ "hours",
32
+ "minutes",
33
+ "seconds",
34
+ "milliseconds",
35
+ ];
36
+
37
+ /**
38
+ * Follows the same format as Intl.DurationFormat.prototype.format().
39
+ *
40
+ * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns
41
+ * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,
42
+ * 7 milliseconds, 8 microseconds, 9 nanoseconds
43
+ * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns
44
+ */
45
+ export type DurationStyle = "short" | "long" | "narrow";
46
+
47
+ export type DurationSuffixMap = {
48
+ short: string;
49
+ shorts: string;
50
+ long: string;
51
+ longs: string;
52
+ narrow: string;
53
+ };
54
+
55
+ export type DurationSuffixType = keyof DurationSuffixMap;
56
+
57
+ export const DURATION_STYLE_SUFFIX_MAP: Record<
58
+ DurationType,
59
+ DurationSuffixMap
60
+ > = {
61
+ days: {
62
+ short: "day",
63
+ shorts: "days",
64
+ long: "day",
65
+ longs: "days",
66
+ narrow: "d",
67
+ },
68
+ hours: {
69
+ short: "hr",
70
+ shorts: "hrs",
71
+ long: "hour",
72
+ longs: "hours",
73
+ narrow: "h",
74
+ },
75
+ minutes: {
76
+ short: "min",
77
+ shorts: "mins",
78
+ long: "minute",
79
+ longs: "minutes",
80
+ narrow: "m",
81
+ },
82
+ seconds: {
83
+ short: "sec",
84
+ shorts: "secs",
85
+ long: "second",
86
+ longs: "seconds",
87
+ narrow: "s",
88
+ },
89
+ milliseconds: {
90
+ short: "ms",
91
+ shorts: "ms",
92
+ long: "millisecond",
93
+ longs: "milliseconds",
94
+ narrow: "ms",
95
+ },
96
+ };
97
+
98
+ function getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {
99
+ return style == "short" ? "shorts" : style === "long" ? "longs" : style;
100
+ }
101
+
102
+ function getValueAndUnitSeparator(style: DurationStyle): string {
103
+ return style === "narrow" ? "" : " ";
104
+ }
105
+
106
+ function getDurationTypeSeparator(style: DurationStyle): string {
107
+ return style === "narrow" ? " " : ", ";
108
+ }
109
+
110
+ /**
111
+ * Convert a milliseconds duration into a Duration object. If the given ms is
112
+ * zero, then return an object with a single field of zero.
113
+ *
114
+ * durationTypeForZero - Defaults to 'milliseconds'
115
+ */
116
+ export function msToDuration(
117
+ ms: number,
118
+ durationTypeForZero?: DurationType,
119
+ ): Duration {
120
+ if (ms === 0) {
121
+ durationTypeForZero = durationTypeForZero ?? "milliseconds";
122
+ return { [durationTypeForZero]: 0 };
123
+ }
124
+
125
+ const duration: Duration = {};
126
+
127
+ for (let i = 0; i < 1; i++) {
128
+ let seconds = Math.floor(ms / MS_PER_SECOND);
129
+ const millis = ms - seconds * MS_PER_SECOND;
130
+
131
+ if (millis > 0) {
132
+ duration["milliseconds"] = millis;
133
+ }
134
+
135
+ if (seconds === 0) {
136
+ break;
137
+ }
138
+
139
+ let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
140
+ seconds -= minutes * SECONDS_PER_MINUTE;
141
+
142
+ if (seconds > 0) {
143
+ duration["seconds"] = seconds;
144
+ }
145
+
146
+ if (minutes === 0) {
147
+ break;
148
+ }
149
+
150
+ let hours = Math.floor(minutes / MINUTES_PER_HOUR);
151
+ minutes -= hours * MINUTES_PER_HOUR;
152
+
153
+ if (minutes > 0) {
154
+ duration["minutes"] = minutes;
155
+ }
156
+
157
+ if (hours === 0) {
158
+ break;
159
+ }
160
+
161
+ const days = Math.floor(hours / HOURS_PER_DAY);
162
+ hours -= days * HOURS_PER_DAY;
163
+
164
+ if (hours > 0) {
165
+ duration["hours"] = hours;
166
+ }
167
+
168
+ if (days > 0) {
169
+ duration["days"] = days;
170
+ }
171
+ }
172
+
173
+ return duration;
174
+ }
175
+
176
+ /**
177
+ * Returns the number of milliseconds for the given duration.
178
+ */
179
+ export function durationToMs(duration: Duration): number {
180
+ const daysMs = (duration.days ?? 0) * MS_PER_DAY;
181
+ const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;
182
+ const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;
183
+ const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;
184
+ const msMs = duration.milliseconds ?? 0;
185
+
186
+ return daysMs + hoursMs + minsMs + secsMs + msMs;
187
+ }
188
+
189
+ /**
190
+ * Format a given Duration object into a string. If the object has no fields,
191
+ * then returns an empty string.
192
+ *
193
+ * style - Defaults to 'short'
194
+ */
195
+ export function formatDuration(duration: Duration, style?: DurationStyle) {
196
+ style = style ?? "short";
197
+ const stylePlural = getDurationStyleForPlural(style);
198
+
199
+ const space = getValueAndUnitSeparator(style);
200
+
201
+ const a: string[] = [];
202
+
203
+ for (const unit of DURATION_TYPE_SEQUENCE) {
204
+ const value = duration[unit];
205
+ if (value === undefined) continue;
206
+
207
+ const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];
208
+ const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];
209
+ a.push(value + space + suffix);
210
+ }
211
+
212
+ const separator = getDurationTypeSeparator(style);
213
+ return a.join(separator);
214
+ }
215
+
216
+ /**
217
+ * Convert a millisecond duration into a human-readable duration string.
218
+ *
219
+ * options.durationTypeForZero - Defaults to 'milliseconds'
220
+ * options.style - Defaults to 'short'
221
+ */
222
+ export function readableDuration(
223
+ ms: number,
224
+ options?: { durationTypeForZero?: DurationType; style?: DurationStyle },
225
+ ): string {
226
+ const duration = msToDuration(ms, options?.durationTypeForZero);
227
+
228
+ return formatDuration(duration, options?.style);
229
+ }
@@ -1,18 +1,63 @@
1
+ import { Duration, durationToMs } from "./duration";
2
+
1
3
  /**
2
4
  * Simple local storage cache with support for auto-expiration.
3
5
  * Note that this works in the browser context only because nodejs does not
4
6
  * have local storage.
7
+ *
8
+ * Create a cache item accessor object with auto-expiration. The value will
9
+ * always be stored as a string by applying JSON.stringify(), and will be
10
+ * returned in the same object type by applying JSON.parse().
11
+ *
12
+ * In order to provide proper type-checking, please always specify the T
13
+ * type parameter. E.g. const item = storeItem<string>("name", 10_000);
14
+ *
15
+ * expires - Either a number in milliseconds, or a Duration object
5
16
  */
6
- export class LocalStorageCache {
7
- public static setValue<T>(key: string, value: T, expireDeltaMs: number) {
8
- const expireMs = Date.now() + expireDeltaMs;
17
+ export function storeItem<T>(
18
+ key: string,
19
+ expires: number | Duration,
20
+ logError = true,
21
+ defaultValue?: T,
22
+ ) {
23
+ const expireDeltaMs =
24
+ typeof expires === "number" ? expires : durationToMs(expires);
25
+
26
+ return new CacheItem<T>(key, expireDeltaMs, logError, defaultValue);
27
+ }
28
+
29
+ class CacheItem<T> {
30
+ /**
31
+ * Create a cache item accessor object with auto-expiration.
32
+ */
33
+ public constructor(
34
+ public readonly key: string,
35
+ public readonly expireDeltaMs: number,
36
+ public readonly logError: boolean,
37
+ defaultValue: T | undefined,
38
+ ) {
39
+ if (defaultValue !== undefined) {
40
+ if (this.get() === undefined) {
41
+ this.set(defaultValue);
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Set the value of this item with auto-expiration.
48
+ */
49
+ public set(value: T): void {
50
+ const expireMs = Date.now() + this.expireDeltaMs;
9
51
  const valueStr = JSON.stringify({ value, expireMs });
10
52
 
11
- globalThis.localStorage.setItem(key, valueStr);
53
+ globalThis.localStorage.setItem(this.key, valueStr);
12
54
  }
13
55
 
14
- public static getValue<T>(key: string, logError = true): T | undefined {
15
- const jsonStr = globalThis.localStorage.getItem(key);
56
+ /**
57
+ * Get the value of this item, or undefined if value is not set or expired.
58
+ */
59
+ public get(): T | undefined {
60
+ const jsonStr = globalThis.localStorage.getItem(this.key);
16
61
 
17
62
  if (!jsonStr || typeof jsonStr !== "string") {
18
63
  return undefined;
@@ -29,41 +74,27 @@ export class LocalStorageCache {
29
74
  typeof obj.expireMs !== "number" ||
30
75
  Date.now() >= obj.expireMs
31
76
  ) {
32
- globalThis.localStorage.removeItem(key);
77
+ globalThis.localStorage.removeItem(this.key);
33
78
  return undefined;
34
79
  }
35
80
 
36
81
  return obj.value;
37
82
  } catch (e) {
38
- if (logError) {
39
- console.error(`Found invalid storage value: ${key}=${jsonStr}:`, e);
83
+ if (this.logError) {
84
+ console.error(
85
+ `Found invalid storage value: ${this.key}=${jsonStr}:`,
86
+ e,
87
+ );
40
88
  }
41
- globalThis.localStorage.removeItem(key);
89
+ globalThis.localStorage.removeItem(this.key);
42
90
  return undefined;
43
91
  }
44
92
  }
45
- }
46
93
 
47
- /** Same as above, but saves some config for reuse. */
48
- export class LocalStorageCacheItem<T> {
49
- public constructor(
50
- public readonly key: string,
51
- public readonly expireDeltaMs: number,
52
- public readonly logError = true,
53
- defaultValue?: T,
54
- ) {
55
- if (defaultValue !== undefined) {
56
- if (this.get() === undefined) {
57
- this.set(defaultValue);
58
- }
59
- }
60
- }
61
-
62
- public set(value: T) {
63
- LocalStorageCache.setValue(this.key, value, this.expireDeltaMs);
64
- }
65
-
66
- public get(): T | undefined {
67
- return LocalStorageCache.getValue(this.key, this.logError);
94
+ /**
95
+ * Remove the value of this item.
96
+ */
97
+ public remove(): void {
98
+ globalThis.localStorage.removeItem(this.key);
68
99
  }
69
100
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Note that month and year do not have fixed durations, and hence are excluded
3
+ * from this file.
4
+ */
5
+
6
+ export const MS_PER_SECOND = 1000;
7
+ export const MS_PER_MINUTE = 60_000;
8
+ export const MS_PER_HOUR = 3_600_000;
9
+ export const MS_PER_DAY = 86_400_000;
10
+ export const MS_PER_WEEK = 604_800_000;
11
+
12
+ export const SECONDS_PER_MINUTE = 60;
13
+ export const SECONDS_PER_HOUR = 3_600;
14
+ export const SECONDS_PER_DAY = 86_400;
15
+ export const SECONDS_PER_WEEK = 604_800;
16
+
17
+ export const MINUTES_PER_HOUR = 60;
18
+ export const MINUTES_PER_DAY = 1440;
19
+ export const MINUTES_PER_WEEK = 10_080;
20
+
21
+ export const HOURS_PER_DAY = 24;
22
+ export const HOURS_PER_WEEK = 168;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Note that month and year do not have fixed durations, and hence are excluded
3
+ * from this file.
4
+ */
5
+ export declare const MS_PER_SECOND = 1000;
6
+ export declare const MS_PER_MINUTE = 60000;
7
+ export declare const MS_PER_HOUR = 3600000;
8
+ export declare const MS_PER_DAY = 86400000;
9
+ export declare const MS_PER_WEEK = 604800000;
10
+ export declare const SECONDS_PER_MINUTE = 60;
11
+ export declare const SECONDS_PER_HOUR = 3600;
12
+ export declare const SECONDS_PER_DAY = 86400;
13
+ export declare const SECONDS_PER_WEEK = 604800;
14
+ export declare const MINUTES_PER_HOUR = 60;
15
+ export declare const MINUTES_PER_DAY = 1440;
16
+ export declare const MINUTES_PER_WEEK = 10080;
17
+ export declare const HOURS_PER_DAY = 24;
18
+ export declare const HOURS_PER_WEEK = 168;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/timeConstants.ts
21
+ var timeConstants_exports = {};
22
+ __export(timeConstants_exports, {
23
+ HOURS_PER_DAY: () => HOURS_PER_DAY,
24
+ HOURS_PER_WEEK: () => HOURS_PER_WEEK,
25
+ MINUTES_PER_DAY: () => MINUTES_PER_DAY,
26
+ MINUTES_PER_HOUR: () => MINUTES_PER_HOUR,
27
+ MINUTES_PER_WEEK: () => MINUTES_PER_WEEK,
28
+ MS_PER_DAY: () => MS_PER_DAY,
29
+ MS_PER_HOUR: () => MS_PER_HOUR,
30
+ MS_PER_MINUTE: () => MS_PER_MINUTE,
31
+ MS_PER_SECOND: () => MS_PER_SECOND,
32
+ MS_PER_WEEK: () => MS_PER_WEEK,
33
+ SECONDS_PER_DAY: () => SECONDS_PER_DAY,
34
+ SECONDS_PER_HOUR: () => SECONDS_PER_HOUR,
35
+ SECONDS_PER_MINUTE: () => SECONDS_PER_MINUTE,
36
+ SECONDS_PER_WEEK: () => SECONDS_PER_WEEK
37
+ });
38
+ module.exports = __toCommonJS(timeConstants_exports);
39
+ var MS_PER_SECOND = 1e3;
40
+ var MS_PER_MINUTE = 6e4;
41
+ var MS_PER_HOUR = 36e5;
42
+ var MS_PER_DAY = 864e5;
43
+ var MS_PER_WEEK = 6048e5;
44
+ var SECONDS_PER_MINUTE = 60;
45
+ var SECONDS_PER_HOUR = 3600;
46
+ var SECONDS_PER_DAY = 86400;
47
+ var SECONDS_PER_WEEK = 604800;
48
+ var MINUTES_PER_HOUR = 60;
49
+ var MINUTES_PER_DAY = 1440;
50
+ var MINUTES_PER_WEEK = 10080;
51
+ var HOURS_PER_DAY = 24;
52
+ var HOURS_PER_WEEK = 168;
53
+ // Annotate the CommonJS export names for ESM import in node:
54
+ 0 && (module.exports = {
55
+ HOURS_PER_DAY,
56
+ HOURS_PER_WEEK,
57
+ MINUTES_PER_DAY,
58
+ MINUTES_PER_HOUR,
59
+ MINUTES_PER_WEEK,
60
+ MS_PER_DAY,
61
+ MS_PER_HOUR,
62
+ MS_PER_MINUTE,
63
+ MS_PER_SECOND,
64
+ MS_PER_WEEK,
65
+ SECONDS_PER_DAY,
66
+ SECONDS_PER_HOUR,
67
+ SECONDS_PER_MINUTE,
68
+ SECONDS_PER_WEEK
69
+ });
70
+ //# sourceMappingURL=timeConstants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/timeConstants.ts"],"sourcesContent":["/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,cAAc;AAEpB,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAEzB,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAEzB,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;","names":[]}