@just-be/wildcard 0.4.0 → 0.5.0

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/README.md CHANGED
@@ -4,14 +4,14 @@ Portable wildcard subdomain routing library with pluggable storage backends. Rou
4
4
 
5
5
  ## Features
6
6
 
7
- - 🔌 **Pluggable storage backends** - Use any file storage (R2, S3, filesystem, etc.)
8
- - 🔒 **Security built-in** - SSRF protection, path traversal prevention, safe header filtering
9
- - 📦 **Three routing modes**:
7
+ - **Pluggable storage backends** - Use any file storage (R2, S3, filesystem, etc.)
8
+ - **Security built-in** - SSRF protection, path traversal prevention, safe header filtering
9
+ - **Three routing modes**:
10
10
  - **Static** - Serve files from any storage backend with SPA mode and custom 404s
11
11
  - **Redirect** - 301/302 redirects to external URLs
12
12
  - **Rewrite** - Proxy requests to external services
13
- - 🏗️ **Framework-agnostic** - Works with Cloudflare Workers, Node.js, Deno, Bun, etc.
14
- - **Type-safe** - Full TypeScript support with Zod validation
13
+ - **Framework-agnostic** - Works with Cloudflare Workers, Node.js, Deno, Bun, etc.
14
+ - **Type-safe** - Full TypeScript support with Zod validation
15
15
 
16
16
  ## Installation
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@just-be/wildcard",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Portable wildcard subdomain routing with pluggable storage backends",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { handleRedirect } from "./redirect";
3
+ import type { RedirectConfig } from "../schemas";
4
+
5
+ function makeRequest(url: string): Request {
6
+ return new Request(url);
7
+ }
8
+
9
+ describe("handleRedirect", () => {
10
+ it("should redirect with 302 by default", async () => {
11
+ const config: RedirectConfig = { type: "redirect", url: "https://example.com" };
12
+ const response = await handleRedirect(makeRequest("https://sub.just-be.dev/"), config);
13
+ expect(response.status).toBe(302);
14
+ expect(response.headers.get("location")).toBe("https://example.com");
15
+ });
16
+
17
+ it("should redirect with 301 when permanent is true", async () => {
18
+ const config: RedirectConfig = {
19
+ type: "redirect",
20
+ url: "https://example.com",
21
+ permanent: true,
22
+ };
23
+ const response = await handleRedirect(makeRequest("https://sub.just-be.dev/"), config);
24
+ expect(response.status).toBe(301);
25
+ expect(response.headers.get("location")).toBe("https://example.com");
26
+ });
27
+
28
+ it("should redirect with 302 when permanent is false", async () => {
29
+ const config: RedirectConfig = {
30
+ type: "redirect",
31
+ url: "https://example.com",
32
+ permanent: false,
33
+ };
34
+ const response = await handleRedirect(makeRequest("https://sub.just-be.dev/"), config);
35
+ expect(response.status).toBe(302);
36
+ });
37
+
38
+ describe("preservePath", () => {
39
+ it("should append path to target URL", async () => {
40
+ const config: RedirectConfig = {
41
+ type: "redirect",
42
+ url: "https://github.com/just-be-dev",
43
+ permanent: true,
44
+ preservePath: true,
45
+ };
46
+ const response = await handleRedirect(makeRequest("https://git.just-be.dev/pds"), config);
47
+ expect(response.status).toBe(301);
48
+ expect(response.headers.get("location")).toBe("https://github.com/just-be-dev/pds");
49
+ });
50
+
51
+ it("should preserve query string", async () => {
52
+ const config: RedirectConfig = {
53
+ type: "redirect",
54
+ url: "https://github.com/just-be-dev",
55
+ preservePath: true,
56
+ };
57
+ const response = await handleRedirect(
58
+ makeRequest("https://git.just-be.dev/pds?tab=readme"),
59
+ config
60
+ );
61
+ expect(response.headers.get("location")).toBe(
62
+ "https://github.com/just-be-dev/pds?tab=readme"
63
+ );
64
+ });
65
+
66
+ it("should handle root path request", async () => {
67
+ const config: RedirectConfig = {
68
+ type: "redirect",
69
+ url: "https://github.com/just-be-dev",
70
+ preservePath: true,
71
+ };
72
+ const response = await handleRedirect(makeRequest("https://git.just-be.dev/"), config);
73
+ expect(response.headers.get("location")).toBe("https://github.com/just-be-dev/");
74
+ });
75
+
76
+ it("should handle target URL with trailing slash", async () => {
77
+ const config: RedirectConfig = {
78
+ type: "redirect",
79
+ url: "https://github.com/just-be-dev/",
80
+ preservePath: true,
81
+ };
82
+ const response = await handleRedirect(makeRequest("https://git.just-be.dev/pds"), config);
83
+ expect(response.headers.get("location")).toBe("https://github.com/just-be-dev/pds");
84
+ });
85
+
86
+ it("should not preserve path when preservePath is not set", async () => {
87
+ const config: RedirectConfig = {
88
+ type: "redirect",
89
+ url: "https://example.com",
90
+ };
91
+ const response = await handleRedirect(
92
+ makeRequest("https://sub.just-be.dev/some/path"),
93
+ config
94
+ );
95
+ expect(response.headers.get("location")).toBe("https://example.com");
96
+ });
97
+ });
98
+ });
@@ -1,7 +1,16 @@
1
1
  import type { Handler } from "../types";
2
2
  import type { RedirectConfig } from "../schemas";
3
3
 
4
- export const handleRedirect: Handler<RedirectConfig> = async (_request, config) => {
4
+ export const handleRedirect: Handler<RedirectConfig> = async (request, config) => {
5
5
  const status = config.permanent ? 301 : 302;
6
+
7
+ if (config.preservePath) {
8
+ const originalUrl = new URL(request.url);
9
+ const targetUrl = new URL(config.url);
10
+ targetUrl.pathname = targetUrl.pathname.replace(/\/$/, "") + originalUrl.pathname;
11
+ targetUrl.search = originalUrl.search;
12
+ return Response.redirect(targetUrl.toString(), status);
13
+ }
14
+
6
15
  return Response.redirect(config.url, status);
7
16
  };
package/src/schemas.ts CHANGED
@@ -46,6 +46,7 @@ export const RedirectConfigSchema = z.object({
46
46
  type: z.literal("redirect"),
47
47
  url: safeUrl(),
48
48
  permanent: z.boolean().optional(),
49
+ preservePath: z.boolean().optional(),
49
50
  });
50
51
 
51
52
  export const RewriteConfigSchema = z.object({