@tim-code/my-util 0.7.1 → 0.7.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tim-code/my-util",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "Tim Sprowl",
package/src/promise.js CHANGED
@@ -109,11 +109,11 @@ export async function allSettled(
109
109
  await limiter?.(elements.length)
110
110
  }
111
111
  if (throws && errors.length) {
112
- const string = errors.map((error) => error.message ?? error).join("; ")
112
+ const string = errors.map((error) => error?.message ?? error).join("; ")
113
113
  const resultError = new PromiseAllError(string)
114
- const { trace } = errors.find((error) => Array.isArray(error.trace)) ?? {}
115
- if (trace) {
116
- resultError.stack = trace.join("\n")
114
+ const { stack } = errors.find((error) => error?.stack) ?? {}
115
+ if (stack) {
116
+ resultError.stack = stack
117
117
  }
118
118
  throw resultError
119
119
  }
@@ -2,7 +2,7 @@
2
2
  import { jest } from "@jest/globals"
3
3
 
4
4
  // Exported API under test:
5
- // - class PollError
5
+ // - classes: PollError, PromiseAllError
6
6
  // - functions: poll, sleep, allSettled, allPatiently, intervalLimiter, alert, throwFirstReject
7
7
 
8
8
  import {
@@ -12,6 +12,7 @@ import {
12
12
  intervalLimiter,
13
13
  poll,
14
14
  PollError,
15
+ PromiseAllError,
15
16
  sleep,
16
17
  throwFirstReject,
17
18
  } from "./promise.js"
@@ -240,13 +241,12 @@ describe("allSettled", () => {
240
241
  expect(result.results.every((r) => r.status === "fulfilled")).toBe(true)
241
242
  })
242
243
 
243
- it("throws joined error message when throws=true and adopts trace stack if provided", async () => {
244
- const eWithTrace = new Error("e1")
245
- eWithTrace.trace = ["trace line 1", "trace line 2"]
244
+ it("throws joined error message when throws=true and adopts stack from first error", async () => {
245
+ const e1 = new Error("e1")
246
246
  const e2 = new Error("third")
247
247
  const arr = [1, 2, 3]
248
248
  const cb = (x) => {
249
- if (x === 1) return Promise.reject(eWithTrace)
249
+ if (x === 1) return Promise.reject(e1)
250
250
  if (x === 2) return x // fulfilled
251
251
  return Promise.reject(e2)
252
252
  }
@@ -256,9 +256,9 @@ describe("allSettled", () => {
256
256
  } catch (e) {
257
257
  thrown = e
258
258
  }
259
- expect(thrown).toBeInstanceOf(Error)
259
+ expect(thrown).toBeInstanceOf(PromiseAllError)
260
260
  expect(thrown.message).toBe("e1; third")
261
- expect(thrown.stack).toBe("trace line 1\ntrace line 2")
261
+ expect(thrown.stack).toBe(e1.stack)
262
262
  })
263
263
  })
264
264
 
package/src/time.js CHANGED
@@ -44,6 +44,16 @@ export function getTime({ floorMinute, timestamp, timeZone = false } = {}) {
44
44
  return getEasternTime({ floorMinute, timestamp, timeZone })
45
45
  }
46
46
 
47
+ /**
48
+ * Get Unix timestamp for a UTC date string.
49
+ * @param {string} utc UTC date time: "YYYY-MM-DDTHH:mm:ssZ" or with UTC offset
50
+ * @returns {number} Seconds since epoch
51
+ */
52
+ export function getUnixTimestamp(utc) {
53
+ const timestamp = Math.floor(new Date(utc).getTime() / 1000)
54
+ return timestamp
55
+ }
56
+
47
57
  /**
48
58
  * Get today's date in YYYY-MM-DD format using local time.
49
59
  * @returns {string}
@@ -96,15 +106,6 @@ export function isDateString(string) {
96
106
  )
97
107
  }
98
108
 
99
- /**
100
- * @deprecated Prefer isDateString()
101
- * @param {string} string
102
- * @returns {boolean}
103
- */
104
- export function isDate(string) {
105
- return isDateString(string)
106
- }
107
-
108
109
  /**
109
110
  * Checks if the string represent a valid HH:mm:ss time.
110
111
  * @param {string} string
@@ -114,15 +115,6 @@ export function isTimeString(string) {
114
115
  return /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/u.test(string)
115
116
  }
116
117
 
117
- /**
118
- * @deprecated Prefer isTimeString()
119
- * @param {string} string
120
- * @returns {boolean}
121
- */
122
- export function isTime(string) {
123
- return isTimeString(string)
124
- }
125
-
126
118
  /**
127
119
  * Checks if a number is a Unix timestamp (i.e. in seconds).
128
120
  * Would not validate timestamps set very far into the future.
package/src/time.test.js CHANGED
@@ -9,9 +9,8 @@ import {
9
9
  getStartOfWeek,
10
10
  getTime,
11
11
  getTimeRange,
12
- isDate,
12
+ getUnixTimestamp,
13
13
  isDateString,
14
- isTime,
15
14
  isTimeString,
16
15
  isUnixTimestamp,
17
16
  today,
@@ -178,6 +177,26 @@ describe("getTime", () => {
178
177
  })
179
178
  })
180
179
 
180
+ describe("getUnixTimestamp", () => {
181
+ test("parses Zulu (UTC) timestamp", () => {
182
+ expect(getUnixTimestamp("2024-06-01T12:34:56Z")).toBe(1717245296)
183
+ })
184
+
185
+ test("parses timestamp with positive offset", () => {
186
+ const expected = Date.UTC(2024, 5, 1, 10, 34, 56) / 1000 // 12:34:56+02:00 == 10:34:56Z
187
+ expect(getUnixTimestamp("2024-06-01T12:34:56+02:00")).toBe(expected)
188
+ })
189
+
190
+ test("floors fractional seconds", () => {
191
+ // 999ms floors down to the same second
192
+ expect(getUnixTimestamp("1970-01-01T00:00:00.999Z")).toBe(0)
193
+ })
194
+
195
+ test("returns NaN for invalid date string", () => {
196
+ expect(Number.isNaN(getUnixTimestamp("not-a-date"))).toBe(true)
197
+ })
198
+ })
199
+
181
200
  describe("today", () => {
182
201
  test("returns today's date in YYYY-MM-DD format", () => {
183
202
  const expected = getTime().date
@@ -247,38 +266,6 @@ describe("isDateString", () => {
247
266
  })
248
267
  })
249
268
 
250
- describe("isDate (deprecated wrapper)", () => {
251
- test("returns true for valid YYYY-MM-DD dates", () => {
252
- expect(isDate("2024-06-01")).toBe(true)
253
- expect(isDate("1999-12-31")).toBe(true)
254
- })
255
-
256
- test("returns false for invalid dates (e.g., 2024-02-31)", () => {
257
- expect(isDate("2024-02-31")).toBe(false)
258
- expect(isDate("2023-04-31")).toBe(false)
259
- })
260
-
261
- test("returns false for invalid formats", () => {
262
- expect(isDate("2024/06/01")).toBe(false)
263
- expect(isDate("06-01-2024")).toBe(false)
264
- expect(isDate("2024-6-1")).toBe(false)
265
- expect(isDate("20240601")).toBe(false)
266
- expect(isDate("abcd-ef-gh")).toBe(false)
267
- })
268
-
269
- test("returns false for impossible months and days", () => {
270
- expect(isDate("2024-00-10")).toBe(false)
271
- expect(isDate("2024-13-10")).toBe(false)
272
- expect(isDate("2024-01-00")).toBe(false)
273
- expect(isDate("2024-01-32")).toBe(false)
274
- })
275
-
276
- test("returns true for leap day", () => {
277
- expect(isDate("2024-02-29")).toBe(true)
278
- expect(isDate("2023-02-29")).toBe(false)
279
- })
280
- })
281
-
282
269
  describe("isTimeString", () => {
283
270
  test("validates correct times", () => {
284
271
  expect(isTimeString("00:00:00")).toBe(true)
@@ -291,26 +278,6 @@ describe("isTimeString", () => {
291
278
  })
292
279
  })
293
280
 
294
- describe("isTime (deprecated wrapper)", () => {
295
- test("returns true for valid HH:mm:ss times", () => {
296
- expect(isTime("00:00:00")).toBe(true)
297
- expect(isTime("23:59:59")).toBe(true)
298
- expect(isTime("12:34:56")).toBe(true)
299
- })
300
-
301
- test("returns false for invalid times", () => {
302
- expect(isTime("24:00:00")).toBe(false)
303
- expect(isTime("12:60:00")).toBe(false)
304
- expect(isTime("12:00:60")).toBe(false)
305
- expect(isTime("1:00:00")).toBe(false)
306
- expect(isTime("12:0:00")).toBe(false)
307
- expect(isTime("12:00:0")).toBe(false)
308
- expect(isTime("12:00")).toBe(false)
309
- expect(isTime("120000")).toBe(false)
310
- expect(isTime("ab:cd:ef")).toBe(false)
311
- })
312
- })
313
-
314
281
  describe("isUnixTimestamp", () => {
315
282
  test("returns true for valid unix timestamps in seconds", () => {
316
283
  expect(isUnixTimestamp(0)).toBe(true)
@@ -428,6 +395,14 @@ describe("getTimeRange", () => {
428
395
  test("handles input with no seconds", () => {
429
396
  expect(getTimeRange("12:00", "12:02")).toEqual(["12:00:00", "12:01:00", "12:02:00"])
430
397
  })
398
+
399
+ // ISSUE: getTimeRange allows a zero step, which would otherwise loop forever; it's capped internally at 1440 iterations.
400
+ test("caps the number of results at MINUTES_IN_DAY when step is zero", () => {
401
+ const result = getTimeRange("12:00:00", "12:00:00", { hours: 0, minutes: 0 })
402
+ expect(result.length).toBe(24 * 60)
403
+ expect(result[0]).toBe("12:00:00")
404
+ expect(result[result.length - 1]).toBe("12:00:00")
405
+ })
431
406
  })
432
407
 
433
408
  describe("addDays", () => {