@padmaj/duration 1.0.1 → 1.0.2

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/dist/index.js CHANGED
@@ -48,7 +48,9 @@ function parseDuration(input) {
48
48
  let matched = false;
49
49
  PARSE_RE.lastIndex = 0;
50
50
  for (const match of str.matchAll(PARSE_RE)) {
51
- total += parseFloat(match[1]) * UNITS[match[2]];
51
+ const value = parseFloat(match[1]) * UNITS[match[2]];
52
+ if (!Number.isFinite(value)) throw new DurationError(input);
53
+ total += value;
52
54
  matched = true;
53
55
  }
54
56
  if (!matched) throw new DurationError(input);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n}\n\nconst PARSE_RE = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/g\n\nexport class DurationError extends Error {\n constructor(input: string) {\n super(`Invalid duration string: \"${input}\"`)\n this.name = 'DurationError'\n }\n}\n\nexport function parseDuration(input: string): number {\n const str = input.trim()\n if (!str) throw new DurationError(input)\n if (str.length > 100) throw new DurationError(input)\n\n let total = 0\n let matched = false\n\n PARSE_RE.lastIndex = 0\n for (const match of str.matchAll(PARSE_RE)) {\n total += parseFloat(match[1]) * UNITS[match[2]]\n matched = true\n }\n\n if (!matched) throw new DurationError(input)\n return Math.round(total)\n}\n\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new RangeError(`formatDuration expects a non-negative finite number, got ${ms}`)\n }\n\n if (ms === 0) return '0ms'\n\n const units: [string, number][] = [\n ['w', UNITS.w],\n ['d', UNITS.d],\n ['h', UNITS.h],\n ['m', UNITS.m],\n ['s', UNITS.s],\n ['ms', UNITS.ms],\n ]\n\n const parts: string[] = []\n let remaining = Math.round(ms)\n\n for (const [unit, value] of units) {\n if (remaining >= value) {\n const count = Math.floor(remaining / value)\n parts.push(`${count}${unit}`)\n remaining %= value\n }\n }\n\n return parts.join(' ')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,QAAgC;AAAA,EACpC,IAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AACN;AAEA,IAAM,WAAW;AAEV,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,OAAe;AACzB,UAAM,6BAA6B,KAAK,GAAG;AAC3C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,cAAc,OAAuB;AACnD,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK;AACvC,MAAI,IAAI,SAAS,IAAK,OAAM,IAAI,cAAc,KAAK;AAEnD,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,WAAS,YAAY;AACrB,aAAW,SAAS,IAAI,SAAS,QAAQ,GAAG;AAC1C,aAAS,WAAW,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;AAC9C,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK;AAC3C,SAAO,KAAK,MAAM,KAAK;AACzB;AAEO,SAAS,eAAe,IAAoB;AACjD,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,GAAG;AAClC,UAAM,IAAI,WAAW,4DAA4D,EAAE,EAAE;AAAA,EACvF;AAEA,MAAI,OAAO,EAAG,QAAO;AAErB,QAAM,QAA4B;AAAA,IAChC,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,MAAM,MAAM,EAAE;AAAA,EACjB;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,YAAY,KAAK,MAAM,EAAE;AAE7B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,QAAI,aAAa,OAAO;AACtB,YAAM,QAAQ,KAAK,MAAM,YAAY,KAAK;AAC1C,YAAM,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE;AAC5B,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,GAAG;AACvB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n}\n\nconst PARSE_RE = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/g\n\nexport class DurationError extends Error {\n constructor(input: string) {\n super(`Invalid duration string: \"${input}\"`)\n this.name = 'DurationError'\n }\n}\n\nexport function parseDuration(input: string): number {\n const str = input.trim()\n if (!str) throw new DurationError(input)\n if (str.length > 100) throw new DurationError(input)\n\n let total = 0\n let matched = false\n\n PARSE_RE.lastIndex = 0\n for (const match of str.matchAll(PARSE_RE)) {\n const value = parseFloat(match[1]) * UNITS[match[2]]\n if (!Number.isFinite(value)) throw new DurationError(input)\n total += value\n matched = true\n }\n\n if (!matched) throw new DurationError(input)\n return Math.round(total)\n}\n\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new RangeError(`formatDuration expects a non-negative finite number, got ${ms}`)\n }\n\n if (ms === 0) return '0ms'\n\n const units: [string, number][] = [\n ['w', UNITS.w],\n ['d', UNITS.d],\n ['h', UNITS.h],\n ['m', UNITS.m],\n ['s', UNITS.s],\n ['ms', UNITS.ms],\n ]\n\n const parts: string[] = []\n let remaining = Math.round(ms)\n\n for (const [unit, value] of units) {\n if (remaining >= value) {\n const count = Math.floor(remaining / value)\n parts.push(`${count}${unit}`)\n remaining %= value\n }\n }\n\n return parts.join(' ')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,QAAgC;AAAA,EACpC,IAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AACN;AAEA,IAAM,WAAW;AAEV,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,OAAe;AACzB,UAAM,6BAA6B,KAAK,GAAG;AAC3C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,cAAc,OAAuB;AACnD,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK;AACvC,MAAI,IAAI,SAAS,IAAK,OAAM,IAAI,cAAc,KAAK;AAEnD,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,WAAS,YAAY;AACrB,aAAW,SAAS,IAAI,SAAS,QAAQ,GAAG;AAC1C,UAAM,QAAQ,WAAW,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;AACnD,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,cAAc,KAAK;AAC1D,aAAS;AACT,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK;AAC3C,SAAO,KAAK,MAAM,KAAK;AACzB;AAEO,SAAS,eAAe,IAAoB;AACjD,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,GAAG;AAClC,UAAM,IAAI,WAAW,4DAA4D,EAAE,EAAE;AAAA,EACvF;AAEA,MAAI,OAAO,EAAG,QAAO;AAErB,QAAM,QAA4B;AAAA,IAChC,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,MAAM,MAAM,EAAE;AAAA,EACjB;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,YAAY,KAAK,MAAM,EAAE;AAE7B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,QAAI,aAAa,OAAO;AACtB,YAAM,QAAQ,KAAK,MAAM,YAAY,KAAK;AAC1C,YAAM,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE;AAC5B,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,GAAG;AACvB;","names":[]}
package/dist/index.mjs CHANGED
@@ -22,7 +22,9 @@ function parseDuration(input) {
22
22
  let matched = false;
23
23
  PARSE_RE.lastIndex = 0;
24
24
  for (const match of str.matchAll(PARSE_RE)) {
25
- total += parseFloat(match[1]) * UNITS[match[2]];
25
+ const value = parseFloat(match[1]) * UNITS[match[2]];
26
+ if (!Number.isFinite(value)) throw new DurationError(input);
27
+ total += value;
26
28
  matched = true;
27
29
  }
28
30
  if (!matched) throw new DurationError(input);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n}\n\nconst PARSE_RE = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/g\n\nexport class DurationError extends Error {\n constructor(input: string) {\n super(`Invalid duration string: \"${input}\"`)\n this.name = 'DurationError'\n }\n}\n\nexport function parseDuration(input: string): number {\n const str = input.trim()\n if (!str) throw new DurationError(input)\n if (str.length > 100) throw new DurationError(input)\n\n let total = 0\n let matched = false\n\n PARSE_RE.lastIndex = 0\n for (const match of str.matchAll(PARSE_RE)) {\n total += parseFloat(match[1]) * UNITS[match[2]]\n matched = true\n }\n\n if (!matched) throw new DurationError(input)\n return Math.round(total)\n}\n\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new RangeError(`formatDuration expects a non-negative finite number, got ${ms}`)\n }\n\n if (ms === 0) return '0ms'\n\n const units: [string, number][] = [\n ['w', UNITS.w],\n ['d', UNITS.d],\n ['h', UNITS.h],\n ['m', UNITS.m],\n ['s', UNITS.s],\n ['ms', UNITS.ms],\n ]\n\n const parts: string[] = []\n let remaining = Math.round(ms)\n\n for (const [unit, value] of units) {\n if (remaining >= value) {\n const count = Math.floor(remaining / value)\n parts.push(`${count}${unit}`)\n remaining %= value\n }\n }\n\n return parts.join(' ')\n}\n"],"mappings":";AAAA,IAAM,QAAgC;AAAA,EACpC,IAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AACN;AAEA,IAAM,WAAW;AAEV,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,OAAe;AACzB,UAAM,6BAA6B,KAAK,GAAG;AAC3C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,cAAc,OAAuB;AACnD,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK;AACvC,MAAI,IAAI,SAAS,IAAK,OAAM,IAAI,cAAc,KAAK;AAEnD,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,WAAS,YAAY;AACrB,aAAW,SAAS,IAAI,SAAS,QAAQ,GAAG;AAC1C,aAAS,WAAW,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;AAC9C,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK;AAC3C,SAAO,KAAK,MAAM,KAAK;AACzB;AAEO,SAAS,eAAe,IAAoB;AACjD,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,GAAG;AAClC,UAAM,IAAI,WAAW,4DAA4D,EAAE,EAAE;AAAA,EACvF;AAEA,MAAI,OAAO,EAAG,QAAO;AAErB,QAAM,QAA4B;AAAA,IAChC,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,MAAM,MAAM,EAAE;AAAA,EACjB;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,YAAY,KAAK,MAAM,EAAE;AAE7B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,QAAI,aAAa,OAAO;AACtB,YAAM,QAAQ,KAAK,MAAM,YAAY,KAAK;AAC1C,YAAM,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE;AAC5B,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,GAAG;AACvB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const UNITS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n}\n\nconst PARSE_RE = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/g\n\nexport class DurationError extends Error {\n constructor(input: string) {\n super(`Invalid duration string: \"${input}\"`)\n this.name = 'DurationError'\n }\n}\n\nexport function parseDuration(input: string): number {\n const str = input.trim()\n if (!str) throw new DurationError(input)\n if (str.length > 100) throw new DurationError(input)\n\n let total = 0\n let matched = false\n\n PARSE_RE.lastIndex = 0\n for (const match of str.matchAll(PARSE_RE)) {\n const value = parseFloat(match[1]) * UNITS[match[2]]\n if (!Number.isFinite(value)) throw new DurationError(input)\n total += value\n matched = true\n }\n\n if (!matched) throw new DurationError(input)\n return Math.round(total)\n}\n\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) {\n throw new RangeError(`formatDuration expects a non-negative finite number, got ${ms}`)\n }\n\n if (ms === 0) return '0ms'\n\n const units: [string, number][] = [\n ['w', UNITS.w],\n ['d', UNITS.d],\n ['h', UNITS.h],\n ['m', UNITS.m],\n ['s', UNITS.s],\n ['ms', UNITS.ms],\n ]\n\n const parts: string[] = []\n let remaining = Math.round(ms)\n\n for (const [unit, value] of units) {\n if (remaining >= value) {\n const count = Math.floor(remaining / value)\n parts.push(`${count}${unit}`)\n remaining %= value\n }\n }\n\n return parts.join(' ')\n}\n"],"mappings":";AAAA,IAAM,QAAgC;AAAA,EACpC,IAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AAAA,EACJ,GAAI;AACN;AAEA,IAAM,WAAW;AAEV,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,OAAe;AACzB,UAAM,6BAA6B,KAAK,GAAG;AAC3C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,cAAc,OAAuB;AACnD,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,IAAK,OAAM,IAAI,cAAc,KAAK;AACvC,MAAI,IAAI,SAAS,IAAK,OAAM,IAAI,cAAc,KAAK;AAEnD,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,WAAS,YAAY;AACrB,aAAW,SAAS,IAAI,SAAS,QAAQ,GAAG;AAC1C,UAAM,QAAQ,WAAW,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;AACnD,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,cAAc,KAAK;AAC1D,aAAS;AACT,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK;AAC3C,SAAO,KAAK,MAAM,KAAK;AACzB;AAEO,SAAS,eAAe,IAAoB;AACjD,MAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,GAAG;AAClC,UAAM,IAAI,WAAW,4DAA4D,EAAE,EAAE;AAAA,EACvF;AAEA,MAAI,OAAO,EAAG,QAAO;AAErB,QAAM,QAA4B;AAAA,IAChC,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,KAAK,MAAM,CAAC;AAAA,IACb,CAAC,MAAM,MAAM,EAAE;AAAA,EACjB;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,YAAY,KAAK,MAAM,EAAE;AAE7B,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,QAAI,aAAa,OAAO;AACtB,YAAM,QAAQ,KAAK,MAAM,YAAY,KAAK;AAC1C,YAAM,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE;AAC5B,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,GAAG;AACvB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@padmaj/duration",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Parse and format duration strings. '2h30m' ↔ 9000000ms. Zero dependencies. TypeScript-first.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",