diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9ae83ea..775eb06 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,24 +1,27 @@ --- name: Bug report about: Create a bug report to help us improve Slonik -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- ## Expected Behavior + ## Current Behavior + ## Possible Solution + ## Steps to Reproduce + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b3c9a9c..cf25cd5 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,19 +1,21 @@ --- name: Feature request about: Create an enhancement request to help us improve Slonik -title: '' +title: "" labels: enhancement -assignees: '' - +assignees: "" --- ## Desired Behavior + ## Motivation + ## Implementation + diff --git a/.github/workflows/feature.yaml b/.github/workflows/feature.yaml index fde9136..cfb3174 100644 --- a/.github/workflows/feature.yaml +++ b/.github/workflows/feature.yaml @@ -11,8 +11,7 @@ jobs: - name: setup node.js uses: actions/setup-node@v2 with: - cache: 'npm' - node-version: '22' + node-version: "22" - run: npm ci - run: npm run lint:eslint - run: npm run lint:tsc @@ -32,7 +31,6 @@ jobs: - name: setup node.js uses: actions/setup-node@v2 with: - cache: 'npm' node-version: ${{ matrix.version }} - run: npm ci - run: npm run test:vitest @@ -54,4 +52,4 @@ on: - opened - synchronize - reopened - - ready_for_review \ No newline at end of file + - ready_for_review diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7e08f53..5186d18 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -11,8 +11,7 @@ jobs: - name: setup node.js uses: actions/setup-node@v2 with: - cache: 'npm' - node-version: '22' + node-version: "22" - run: npm ci - run: npm run lint:eslint - run: npm run lint:tsc @@ -27,4 +26,4 @@ name: Test, build and release on: push: branches: - - main \ No newline at end of file + - main diff --git a/README.md b/README.md index c54104f..5d291eb 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,11 @@ The frustration of seeing code littered with `86_400` and `60 * 60 * 24` and sim ## Usage ```ts -import { - getDuration, - type HumanDuration, - type IntervalName, -} from 'hdinf'; +import { getDuration, type HumanDuration, type IntervalName } from "hdinf"; // parseDuration(duration: HumanDuration, interval: IntervalName): number -getDuration('1 day', 'seconds'); -getDuration('1 day 2 hours 3 seconds', 'milliseconds'); +getDuration("1 day", "seconds"); +getDuration("1 day 2 hours 3 seconds", "milliseconds"); ``` In the wild, you should use this library remove any hard-coded durations from your codebase, e.g., @@ -41,15 +37,15 @@ This will reduce the number of bugs that are introduced by passing in a duration One of the benefits of this library is that the input format is enforced using TypeScript template literal types, i.e. the compiler will complain if you pass in an invalid duration. ```ts -import { getDuration } from 'hdinf'; +import { getDuration } from "hdinf"; -getDuration('1 day', 'seconds'); // OK -getDuration('1 day', 'milliseconds'); // OK -getDuration('1 day', 'minutes'); // OK -getDuration('1 day', 'hours'); // OK +getDuration("1 day", "seconds"); // OK +getDuration("1 day", "milliseconds"); // OK +getDuration("1 day", "minutes"); // OK +getDuration("1 day", "hours"); // OK -getDuration('1 foo', 'seconds'); // TS error because foo is not a valid time period -getDuration('1 hour 1 day', 'seconds'); // TS error because lesser units cannot precede greater units (hour < day) +getDuration("1 foo", "seconds"); // TS error because foo is not a valid time period +getDuration("1 hour 1 day", "seconds"); // TS error because lesser units cannot precede greater units (hour < day) ``` ## Abbreviations @@ -60,12 +56,21 @@ Abbreviations are intentionally not supported. The goal of this library is to re `hdinf` was primarily designed with the intent of replacing the use of constants rather than allowing dynamic expressions. +### `dayjs` + If you are already using a library like [dayjs](https://day.js.org/), you can probably find a [native solution](https://day.js.org/docs/en/plugin/duration) for this problem, e.g., ```ts -import dayjs from 'dayjs'; -import duration from 'dayjs/plugin/duration'; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; dayjs.extend(duration); -dayjs.duration(100, 'days'); -``` \ No newline at end of file +dayjs.duration(100, "days"); +``` + +### `ms` + +My grudge with [ms](https://github.com/vercel/ms) is that: + +- it allows arbitrary formats (so you end up with `1ms`, `1msec`, `1millisecond`, etc) +- it only allows to express output in milliseconds, so you end up with `/ 1000` and similar expressions diff --git a/cspell.yaml b/cspell.yaml index 426a19b..1beaed7 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -6,4 +6,4 @@ words: - gajus - kuizinas - vitest - - hdinf \ No newline at end of file + - hdinf diff --git a/package-lock.json b/package-lock.json index 0d59af7..d9ec7b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "semantic-url-parser", + "name": "hdinf", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "semantic-url-parser", + "name": "hdinf", "version": "1.0.0", "license": "BSD-3-Clause", "devDependencies": { diff --git a/package.json b/package.json index 0e85be0..78bcd6e 100644 --- a/package.json +++ b/package.json @@ -54,4 +54,4 @@ }, "types": "./dist/index.d.ts", "version": "1.0.0" -} \ No newline at end of file +} diff --git a/src/getDuration.test.ts b/src/getDuration.test.ts index cff3765..b7b95ee 100644 --- a/src/getDuration.test.ts +++ b/src/getDuration.test.ts @@ -6,9 +6,11 @@ test('single duration', () => { expect(getDuration('1 day', 'milliseconds')).toBe(86_400_000); expect(getDuration('1 day', 'minutes')).toBe(1_440); expect(getDuration('1 day', 'hours')).toBe(24); +}); +test('invalid duration', () => { // @ts-expect-error - expect TS to complain about invalid duration - getDuration('1 foo', 'seconds'); + expect(() => getDuration('1 foo', 'seconds')).toThrow(); }); test('multiple durations', () => { @@ -18,7 +20,9 @@ test('multiple durations', () => { ); expect(getDuration('1 day 2 hours', 'minutes')).toBe(1_440 + 2 * 60); expect(getDuration('1 day 2 hours', 'hours')).toBe(24 + 2); +}); +test('invalid multiple durations', () => { // @ts-expect-error - expect TS to complain about invalid duration - getDuration('1 day 2 foos', 'seconds'); + expect(() => getDuration('1 day 2 foos', 'seconds')).toThrow(); }); diff --git a/src/getDuration.ts b/src/getDuration.ts index f82f177..f140447 100644 --- a/src/getDuration.ts +++ b/src/getDuration.ts @@ -56,22 +56,23 @@ export const getDuration = ( ttl: HumanDuration, format: IntervalName, ): number => { - const regex = - // eslint-disable-next-line unicorn/no-unsafe-regex - /(\d+(?:\.\d+)?) (milliseconds?|seconds?|minutes?|hours?|days?)/gu; + if (!ttl || typeof ttl !== 'string') { + throw new Error('Invalid duration format'); + } let totalMilliseconds = 0; + const parts = ttl.split(' '); - let match; + for (let index = 0; index < parts.length; index += 2) { + const value = Number.parseFloat(parts[index]); + const interval = parts[index + 1] as IntervalName; - while ((match = regex.exec(ttl)) !== null) { - const value = Number.parseFloat(match[1]); - const interval = match[2] as IntervalName; + if (Number.isNaN(value) || !timeMultipliers[interval]) { + throw new Error(`Invalid duration part: ${parts[index]} ${interval}`); + } totalMilliseconds += value * timeMultipliers[interval]; } - const result = totalMilliseconds / timeMultipliers[format]; - - return result; + return totalMilliseconds / timeMultipliers[format]; }; diff --git a/tsconfig.build.json b/tsconfig.build.json index 4cc165f..33cba63 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -7,4 +7,4 @@ "include": [ "src" ] -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index d186ee6..57b1d9c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,4 +21,4 @@ "include": [ "src" ] -} \ No newline at end of file +}