@oricardopestana/ts-utils 1.0.0 → 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.
@@ -0,0 +1,51 @@
1
+ name: Test & Publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: 22.13
17
+ - uses: pnpm/action-setup@v4
18
+ with:
19
+ version: latest
20
+ - run: pnpm install --frozen-lockfile
21
+ - run: pnpm test
22
+
23
+ publish:
24
+ needs: test
25
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ contents: write
29
+ id-token: write
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: actions/setup-node@v4
33
+ with:
34
+ node-version: 22.13
35
+ - uses: pnpm/action-setup@v4
36
+ with:
37
+ version: latest
38
+ - run: pnpm install --frozen-lockfile
39
+
40
+ - run: npm version patch --no-git-tag-version
41
+
42
+ - run: |
43
+ git config user.name "github-actions[bot]"
44
+ git config user.email "github-actions[bot]@users.noreply.github.com"
45
+ git add package.json
46
+ git commit -m "chore: bump version to $(node -p "require('./package.json').version") [skip ci]"
47
+ git tag v$(node -p "require('./package.json').version")
48
+ git push --tags
49
+ git push
50
+
51
+ - run: pnpm publish --provenance --access public
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # ts-utils
package/global.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare global {
2
+ type Nullable<T> = T | null;
3
+ type FetchError = { statusCode: number; statusMessage: string };
4
+ }
5
+
6
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oricardopestana/ts-utils",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "TypeScript utilities",
5
5
  "homepage": "https://github.com/oricardopestana/ts-utils#readme",
6
6
  "bugs": {
@@ -14,7 +14,10 @@
14
14
  "author": "Ricardo Pestana (https://github.com/oricardopestana)",
15
15
  "type": "module",
16
16
  "main": "index.js",
17
+ "dependencies": {
18
+ "vitest": "^4.1.8"
19
+ },
17
20
  "scripts": {
18
- "test": "echo \"Error: no test specified\" && exit 1"
21
+ "test": "vitest run"
19
22
  }
20
- }
23
+ }
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { useAsync } from "./index";
3
+
4
+ describe("useAsync", () => {
5
+ it("should return [data, null] when the promise resolves", async () => {
6
+ const [data, error] = await useAsync(() => Promise.resolve(42));
7
+ expect(data).toBe(42);
8
+ expect(error).toBeNull();
9
+ });
10
+
11
+ it("should return [null, reason] when the promise rejects with an Error", async () => {
12
+ const [data, error] = await useAsync(() => Promise.reject(new Error("Something went wrong")));
13
+ expect(data).toBeNull();
14
+ expect(error).toBeInstanceOf(Error);
15
+ expect((error as Error).message).toBe("Something went wrong");
16
+ });
17
+
18
+ it("should return [null, reason] when the promise rejects with a string", async () => {
19
+ const [data, error] = await useAsync(() => Promise.reject("Oops!"));
20
+ expect(data).toBeNull();
21
+ expect(error).toBe("Oops!");
22
+ });
23
+
24
+ it("should return [null, reason] when the promise rejects with a number", async () => {
25
+ const [data, error] = await useAsync(() => Promise.reject(404));
26
+ expect(data).toBeNull();
27
+ expect(error).toBe(404);
28
+ });
29
+
30
+ it("should handle a callback that throws synchronously", async () => {
31
+ const [data, error] = await useAsync(() => {
32
+ throw new Error("Sync throw");
33
+ });
34
+ expect(data).toBeNull();
35
+ expect(error).toBeInstanceOf(Error);
36
+ expect((error as Error).message).toBe("Sync throw");
37
+ });
38
+
39
+ it("should handle resolved undefined", async () => {
40
+ const [data, error] = await useAsync(() => Promise.resolve(undefined));
41
+ expect(data).toBeUndefined();
42
+ expect(error).toBeNull();
43
+ });
44
+
45
+ it("should handle resolved null", async () => {
46
+ const [data, error] = await useAsync(() => Promise.resolve(null));
47
+ expect(data).toBeNull();
48
+ expect(error).toBeNull();
49
+ });
50
+
51
+ it("should handle an empty async function", async () => {
52
+ const [data, error] = await useAsync(async () => {});
53
+ expect(data).toBeUndefined();
54
+ expect(error).toBeNull();
55
+ });
56
+ });
@@ -0,0 +1,26 @@
1
+ export type AsyncResult<ResolveData, RejectionReason> =
2
+ | [data: null, reason: RejectionReason]
3
+ | [data: ResolveData, reason: null];
4
+
5
+ /**
6
+ * Gracefully handles a callback that returns a promise.
7
+ *
8
+ * @example
9
+ * await useAsync(() => Promise.resolve(123))
10
+ * // [123, null]
11
+ *
12
+ * await useAsync(() => Promise.reject(new Error('Error message')))
13
+ * // [null, new Error('Oops!')]
14
+ */
15
+ export async function useAsync<ResolveData = unknown, RejectionReason = Error>(
16
+ callback: () => Promise<ResolveData>,
17
+ ): Promise<AsyncResult<ResolveData, RejectionReason>> {
18
+ try {
19
+ const data = await callback().catch((error) => {
20
+ throw error;
21
+ });
22
+ return [data, null];
23
+ } catch (error: unknown) {
24
+ return [null, error as RejectionReason];
25
+ }
26
+ }