@shetty4l/core 0.1.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.
@@ -0,0 +1,152 @@
1
+ import { afterAll, describe, expect, test } from "bun:test";
2
+ import {
3
+ corsHeaders,
4
+ corsPreflightResponse,
5
+ createServer,
6
+ healthResponse,
7
+ jsonError,
8
+ jsonOk,
9
+ } from "../src/http";
10
+
11
+ // --- corsHeaders ---
12
+
13
+ describe("corsHeaders", () => {
14
+ test("returns CORS headers", () => {
15
+ const headers = corsHeaders();
16
+ expect(headers["Access-Control-Allow-Origin"]).toBe("*");
17
+ expect(headers["Access-Control-Allow-Methods"]).toContain("GET");
18
+ expect(headers["Access-Control-Allow-Headers"]).toContain("Content-Type");
19
+ });
20
+
21
+ test("returns a fresh copy each time", () => {
22
+ const a = corsHeaders();
23
+ const b = corsHeaders();
24
+ expect(a).toEqual(b);
25
+ a["X-Custom"] = "mutated";
26
+ expect(b["X-Custom"]).toBeUndefined();
27
+ });
28
+ });
29
+
30
+ // --- corsPreflightResponse ---
31
+
32
+ describe("corsPreflightResponse", () => {
33
+ test("returns 204 with CORS headers", () => {
34
+ const res = corsPreflightResponse();
35
+ expect(res.status).toBe(204);
36
+ expect(res.headers.get("Access-Control-Allow-Origin")).toBe("*");
37
+ });
38
+ });
39
+
40
+ // --- jsonOk ---
41
+
42
+ describe("jsonOk", () => {
43
+ test("returns JSON with 200 by default", async () => {
44
+ const res = jsonOk({ message: "hello" });
45
+ expect(res.status).toBe(200);
46
+ const body = await res.json();
47
+ expect(body).toEqual({ message: "hello" });
48
+ });
49
+
50
+ test("supports custom status code", () => {
51
+ const res = jsonOk({ id: 1 }, 201);
52
+ expect(res.status).toBe(201);
53
+ });
54
+
55
+ test("includes CORS headers", () => {
56
+ const res = jsonOk({});
57
+ expect(res.headers.get("Access-Control-Allow-Origin")).toBe("*");
58
+ });
59
+ });
60
+
61
+ // --- jsonError ---
62
+
63
+ describe("jsonError", () => {
64
+ test("returns error JSON", async () => {
65
+ const res = jsonError(400, "bad request");
66
+ expect(res.status).toBe(400);
67
+ const body = await res.json();
68
+ expect(body).toEqual({ error: "bad request" });
69
+ });
70
+
71
+ test("includes CORS headers", () => {
72
+ const res = jsonError(500, "oops");
73
+ expect(res.headers.get("Access-Control-Allow-Origin")).toBe("*");
74
+ });
75
+ });
76
+
77
+ // --- healthResponse ---
78
+
79
+ describe("healthResponse", () => {
80
+ test("returns standard health fields", async () => {
81
+ const startTime = Date.now() - 5000;
82
+ const res = healthResponse("1.0.0", startTime);
83
+ expect(res.status).toBe(200);
84
+ const body = (await res.json()) as Record<string, unknown>;
85
+ expect(body.status).toBe("healthy");
86
+ expect(body.version).toBe("1.0.0");
87
+ expect(typeof body.uptime).toBe("number");
88
+ expect((body.uptime as number) >= 4).toBe(true);
89
+ });
90
+
91
+ test("includes extra fields when provided", async () => {
92
+ const res = healthResponse("1.0.0", Date.now(), {
93
+ memories: 42,
94
+ });
95
+ const body = (await res.json()) as Record<string, unknown>;
96
+ expect(body.memories).toBe(42);
97
+ });
98
+ });
99
+
100
+ // --- createServer ---
101
+
102
+ describe("createServer", () => {
103
+ let server: ReturnType<typeof createServer> | null = null;
104
+
105
+ afterAll(() => {
106
+ if (server) server.stop();
107
+ });
108
+
109
+ test("starts server and responds to /health", async () => {
110
+ server = createServer({
111
+ port: 0, // random available port
112
+ version: "0.1.0-test",
113
+ onRequest: () => null,
114
+ });
115
+
116
+ const res = await fetch(`http://localhost:${server.port}/health`);
117
+ expect(res.status).toBe(200);
118
+ const body = (await res.json()) as Record<string, unknown>;
119
+ expect(body.status).toBe("healthy");
120
+ expect(body.version).toBe("0.1.0-test");
121
+ });
122
+
123
+ test("handles OPTIONS preflight", async () => {
124
+ const res = await fetch(`http://localhost:${server!.port}/anything`, {
125
+ method: "OPTIONS",
126
+ });
127
+ expect(res.status).toBe(204);
128
+ expect(res.headers.get("Access-Control-Allow-Origin")).toBe("*");
129
+ });
130
+
131
+ test("routes to onRequest for custom paths", async () => {
132
+ server!.stop();
133
+ server = createServer({
134
+ port: 0,
135
+ version: "0.1.0-test",
136
+ onRequest: (_req, url) => {
137
+ if (url.pathname === "/echo") return jsonOk({ path: "/echo" });
138
+ return null;
139
+ },
140
+ });
141
+
142
+ const res = await fetch(`http://localhost:${server.port}/echo`);
143
+ expect(res.status).toBe(200);
144
+ const body = (await res.json()) as Record<string, unknown>;
145
+ expect(body.path).toBe("/echo");
146
+ });
147
+
148
+ test("returns 404 when onRequest returns null", async () => {
149
+ const res = await fetch(`http://localhost:${server!.port}/nonexistent`);
150
+ expect(res.status).toBe(404);
151
+ });
152
+ });
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { err, ok } from "../src/result";
3
+
4
+ describe("ok", () => {
5
+ test("creates Ok result", () => {
6
+ const result = ok(42);
7
+ expect(result.ok).toBe(true);
8
+ expect(result.value).toBe(42);
9
+ });
10
+
11
+ test("works with string values", () => {
12
+ const result = ok("hello");
13
+ expect(result.ok).toBe(true);
14
+ expect(result.value).toBe("hello");
15
+ });
16
+
17
+ test("works with objects", () => {
18
+ const result = ok({ name: "test" });
19
+ expect(result.ok).toBe(true);
20
+ expect(result.value).toEqual({ name: "test" });
21
+ });
22
+ });
23
+
24
+ describe("err", () => {
25
+ test("creates Err result", () => {
26
+ const result = err("something went wrong");
27
+ expect(result.ok).toBe(false);
28
+ expect(result.error).toBe("something went wrong");
29
+ });
30
+
31
+ test("works with structured errors", () => {
32
+ const result = err({ code: "NOT_FOUND", message: "missing" });
33
+ expect(result.ok).toBe(false);
34
+ expect(result.error).toEqual({ code: "NOT_FOUND", message: "missing" });
35
+ });
36
+ });
37
+
38
+ describe("type narrowing", () => {
39
+ test("narrows Ok in conditional", () => {
40
+ const result = ok(42);
41
+ if (result.ok) {
42
+ // TypeScript knows this is Ok<number>
43
+ expect(result.value).toBe(42);
44
+ } else {
45
+ throw new Error("should not reach");
46
+ }
47
+ });
48
+
49
+ test("narrows Err in conditional", () => {
50
+ const result = err("bad");
51
+ if (!result.ok) {
52
+ // TypeScript knows this is Err<string>
53
+ expect(result.error).toBe("bad");
54
+ } else {
55
+ throw new Error("should not reach");
56
+ }
57
+ });
58
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { onShutdown } from "../src/signals";
3
+
4
+ describe("onShutdown", () => {
5
+ test("registers handler without throwing", () => {
6
+ // onShutdown registers process listeners — verify it doesn't throw.
7
+ // We can't easily test signal delivery in unit tests, but we can
8
+ // verify the function accepts valid inputs.
9
+ expect(() => {
10
+ onShutdown(() => {}, { signals: [] });
11
+ }).not.toThrow();
12
+ });
13
+
14
+ test("accepts async cleanup function", () => {
15
+ expect(() => {
16
+ onShutdown(async () => {}, { signals: [] });
17
+ }).not.toThrow();
18
+ });
19
+
20
+ test("accepts custom signals list", () => {
21
+ expect(() => {
22
+ onShutdown(() => {}, { signals: ["SIGUSR1"], timeoutMs: 1000 });
23
+ }).not.toThrow();
24
+ });
25
+ });
@@ -0,0 +1,55 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { readVersion } from "../src/version";
5
+
6
+ const TMP = join(import.meta.dir, ".tmp-version");
7
+
8
+ function setup() {
9
+ if (existsSync(TMP)) rmSync(TMP, { recursive: true });
10
+ mkdirSync(TMP, { recursive: true });
11
+ }
12
+
13
+ function teardown() {
14
+ if (existsSync(TMP)) rmSync(TMP, { recursive: true });
15
+ }
16
+
17
+ describe("readVersion", () => {
18
+ test("returns fallback when VERSION file missing", () => {
19
+ setup();
20
+ try {
21
+ expect(readVersion(TMP)).toBe("0.0.0-dev");
22
+ } finally {
23
+ teardown();
24
+ }
25
+ });
26
+
27
+ test("returns custom fallback when VERSION file missing", () => {
28
+ setup();
29
+ try {
30
+ expect(readVersion(TMP, "1.0.0-local")).toBe("1.0.0-local");
31
+ } finally {
32
+ teardown();
33
+ }
34
+ });
35
+
36
+ test("reads VERSION file when present", () => {
37
+ setup();
38
+ try {
39
+ writeFileSync(join(TMP, "VERSION"), "2.3.4\n");
40
+ expect(readVersion(TMP)).toBe("2.3.4");
41
+ } finally {
42
+ teardown();
43
+ }
44
+ });
45
+
46
+ test("trims whitespace from VERSION file", () => {
47
+ setup();
48
+ try {
49
+ writeFileSync(join(TMP, "VERSION"), " 1.0.0 \n\n");
50
+ expect(readVersion(TMP)).toBe("1.0.0");
51
+ } finally {
52
+ teardown();
53
+ }
54
+ });
55
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "types": ["bun"]
15
+ },
16
+ "include": ["src/**/*", "test/**/*"],
17
+ "exclude": ["node_modules"]
18
+ }