bxo 0.0.5-dev.7 → 0.0.5-dev.71
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 +145 -499
- package/example/cors-example.ts +49 -0
- package/example/index.html +5 -0
- package/example/index.ts +191 -0
- package/example/openapi-example.ts +132 -0
- package/package.json +8 -8
- package/plugins/cors.ts +123 -73
- package/plugins/index.ts +2 -11
- package/plugins/openapi.ts +203 -0
- package/src/index.ts +644 -0
- package/tsconfig.json +3 -5
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +0 -111
- package/example.ts +0 -183
- package/index.ts +0 -833
- package/plugins/auth.ts +0 -119
- package/plugins/logger.ts +0 -109
- package/plugins/ratelimit.ts +0 -140
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import BXO from "../src/index";
|
|
2
|
+
import { cors } from "../plugins";
|
|
3
|
+
|
|
4
|
+
const app = new BXO();
|
|
5
|
+
|
|
6
|
+
// Use the CORS plugin
|
|
7
|
+
app.use(cors({
|
|
8
|
+
origin: ["http://localhost:3000", "http://localhost:3001"],
|
|
9
|
+
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
10
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
11
|
+
credentials: true
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
// Add some routes
|
|
15
|
+
app.get("/api/users", async (ctx) => {
|
|
16
|
+
return ctx.json([
|
|
17
|
+
{ id: 1, name: "John Doe" },
|
|
18
|
+
{ id: 2, name: "Jane Smith" }
|
|
19
|
+
]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
app.post("/api/users", async (ctx) => {
|
|
23
|
+
const user = ctx.body as { name: string };
|
|
24
|
+
return ctx.json({ id: 3, name: user.name }, 201);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Custom beforeRequest hook example
|
|
28
|
+
app.beforeRequest(async (req) => {
|
|
29
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
|
|
30
|
+
return req; // Continue with the request
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Custom afterRequest hook example
|
|
34
|
+
app.afterRequest(async (req, res) => {
|
|
35
|
+
console.log(`[${new Date().toISOString()}] Response: ${res.status}`);
|
|
36
|
+
return res; // Return the modified response
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Custom error handler
|
|
40
|
+
app.onError(async (error, req) => {
|
|
41
|
+
console.error(`Error handling ${req.method} ${req.url}:`, error);
|
|
42
|
+
return new Response("Something went wrong", { status: 500 });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Start the server
|
|
46
|
+
app.start();
|
|
47
|
+
|
|
48
|
+
console.log("Server running on http://localhost:3000");
|
|
49
|
+
console.log("Try making a CORS request from another origin!");
|
package/example/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import BXO, { z } from "../src";
|
|
2
|
+
import index from "./index.html";
|
|
3
|
+
import { openapi } from "../plugins/openapi";
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const bxo = new BXO();
|
|
7
|
+
|
|
8
|
+
bxo.default("/", index);
|
|
9
|
+
bxo.default("/*", index);
|
|
10
|
+
|
|
11
|
+
// API routes with comprehensive metadata
|
|
12
|
+
bxo.get("/api/get/:id", (ctx) => {
|
|
13
|
+
return new Response(ctx.params.id + ctx.query.name, {
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "text/html"
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}, {
|
|
19
|
+
query: z.object({
|
|
20
|
+
name: z.number()
|
|
21
|
+
}),
|
|
22
|
+
response: {
|
|
23
|
+
200: z.object({
|
|
24
|
+
name: z.string()
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
detail: {
|
|
28
|
+
tags: ["API"],
|
|
29
|
+
summary: "Get data by ID",
|
|
30
|
+
description: "Retrieve data using an ID and name query parameter",
|
|
31
|
+
params: {
|
|
32
|
+
id: z.string().describe("Unique identifier for the data")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
bxo.post("/api/post", (ctx) => {
|
|
38
|
+
console.log(ctx.body)
|
|
39
|
+
return new Response("Hello" + ctx.body.name, {
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "text/html"
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}, {
|
|
45
|
+
detail: {
|
|
46
|
+
tags: ["API"],
|
|
47
|
+
summary: "Create new data",
|
|
48
|
+
description: "Submit new data with name and avatar file",
|
|
49
|
+
defaultContentType: "multipart/form-data"
|
|
50
|
+
},
|
|
51
|
+
body: z.object({
|
|
52
|
+
name: z.string(),
|
|
53
|
+
avatar: z.file()
|
|
54
|
+
}),
|
|
55
|
+
response: {
|
|
56
|
+
200: z.object({
|
|
57
|
+
name: z.string()
|
|
58
|
+
}),
|
|
59
|
+
400: z.object({
|
|
60
|
+
error: z.string()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Additional routes to showcase different features
|
|
66
|
+
bxo.get("/api/users", (ctx) => {
|
|
67
|
+
return ctx.json({ users: ["John", "Jane", "Bob"] });
|
|
68
|
+
}, {
|
|
69
|
+
detail: {
|
|
70
|
+
tags: ["Users"],
|
|
71
|
+
summary: "Get all users",
|
|
72
|
+
description: "Retrieve a list of all users in the system"
|
|
73
|
+
},
|
|
74
|
+
response: {
|
|
75
|
+
200: z.object({
|
|
76
|
+
users: z.array(z.string())
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
bxo.get("/api/users/:id", (ctx) => {
|
|
82
|
+
const id = ctx.params.id;
|
|
83
|
+
return ctx.json({ user: { id, name: "John Doe", email: "john@example.com" } });
|
|
84
|
+
}, {
|
|
85
|
+
detail: {
|
|
86
|
+
tags: ["Users"],
|
|
87
|
+
summary: "Get user by ID",
|
|
88
|
+
description: "Retrieve a specific user by their unique identifier",
|
|
89
|
+
params: {
|
|
90
|
+
id: z.string().describe("User's unique identifier")
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
response: {
|
|
94
|
+
200: z.object({
|
|
95
|
+
user: z.object({
|
|
96
|
+
id: z.string(),
|
|
97
|
+
name: z.string(),
|
|
98
|
+
email: z.string()
|
|
99
|
+
})
|
|
100
|
+
}),
|
|
101
|
+
404: z.object({
|
|
102
|
+
error: z.string()
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
bxo.post("/api/users", (ctx) => {
|
|
108
|
+
const userData = ctx.body;
|
|
109
|
+
return ctx.json({ message: "User created", user: userData });
|
|
110
|
+
}, {
|
|
111
|
+
detail: {
|
|
112
|
+
tags: ["Users"],
|
|
113
|
+
summary: "Create new user",
|
|
114
|
+
description: "Create a new user account with the provided information"
|
|
115
|
+
},
|
|
116
|
+
body: z.object({
|
|
117
|
+
name: z.string().min(1, "Name is required"),
|
|
118
|
+
email: z.string().email("Invalid email format"),
|
|
119
|
+
age: z.number().min(18, "Must be at least 18 years old").optional()
|
|
120
|
+
}),
|
|
121
|
+
response: {
|
|
122
|
+
201: z.object({
|
|
123
|
+
message: z.string(),
|
|
124
|
+
user: z.object({
|
|
125
|
+
name: z.string(),
|
|
126
|
+
email: z.string(),
|
|
127
|
+
age: z.number().optional()
|
|
128
|
+
})
|
|
129
|
+
}),
|
|
130
|
+
400: z.object({
|
|
131
|
+
error: z.string(),
|
|
132
|
+
issues: z.array(z.any()).optional()
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Health check route
|
|
138
|
+
bxo.get("/health", (ctx) => {
|
|
139
|
+
return ctx.json({
|
|
140
|
+
status: "ok",
|
|
141
|
+
timestamp: new Date().toISOString(),
|
|
142
|
+
uptime: process.uptime()
|
|
143
|
+
});
|
|
144
|
+
}, {
|
|
145
|
+
detail: {
|
|
146
|
+
tags: ["System"],
|
|
147
|
+
summary: "Health check",
|
|
148
|
+
description: "Check the health status of the API server"
|
|
149
|
+
},
|
|
150
|
+
response: {
|
|
151
|
+
200: z.object({
|
|
152
|
+
status: z.string(),
|
|
153
|
+
timestamp: z.string(),
|
|
154
|
+
uptime: z.number()
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Use OpenAPI plugin with enhanced configuration
|
|
160
|
+
bxo.use(openapi({
|
|
161
|
+
path: "/docs",
|
|
162
|
+
jsonPath: "/openapi.json",
|
|
163
|
+
defaultTags: ["API"],
|
|
164
|
+
securitySchemes: {
|
|
165
|
+
bearerAuth: {
|
|
166
|
+
type: "http",
|
|
167
|
+
scheme: "bearer",
|
|
168
|
+
bearerFormat: "JWT",
|
|
169
|
+
description: "JWT token for authentication"
|
|
170
|
+
},
|
|
171
|
+
apiKeyAuth: {
|
|
172
|
+
type: "apiKey",
|
|
173
|
+
in: "header",
|
|
174
|
+
name: "X-API-Key",
|
|
175
|
+
description: "API key for authentication"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
globalSecurity: [
|
|
179
|
+
{ bearerAuth: [] },
|
|
180
|
+
{ apiKeyAuth: [] }
|
|
181
|
+
],
|
|
182
|
+
openapiConfig: {}
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
bxo.start();
|
|
186
|
+
console.log(`Server is running on http://localhost:${bxo.server?.port}`);
|
|
187
|
+
console.log(`OpenAPI documentation available at http://localhost:${bxo.server?.port}/docs`);
|
|
188
|
+
console.log(`OpenAPI JSON available at http://localhost:${bxo.server?.port}/openapi.json`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
main();
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import BXO, { z } from "../src";
|
|
2
|
+
import { openapi } from "../plugins/openapi";
|
|
3
|
+
|
|
4
|
+
// Create a BXO app with OpenAPI plugin
|
|
5
|
+
const app = new BXO()
|
|
6
|
+
.use(openapi({
|
|
7
|
+
path: "/docs",
|
|
8
|
+
jsonPath: "/openapi.json",
|
|
9
|
+
defaultTags: ["API"],
|
|
10
|
+
securitySchemes: {
|
|
11
|
+
bearerAuth: {
|
|
12
|
+
type: "http",
|
|
13
|
+
scheme: "bearer",
|
|
14
|
+
bearerFormat: "JWT",
|
|
15
|
+
description: "JWT token for authentication"
|
|
16
|
+
},
|
|
17
|
+
apiKeyAuth: {
|
|
18
|
+
type: "apiKey",
|
|
19
|
+
in: "header",
|
|
20
|
+
name: "X-API-Key",
|
|
21
|
+
description: "API key for authentication"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
globalSecurity: [
|
|
25
|
+
{ bearerAuth: [] },
|
|
26
|
+
{ apiKeyAuth: [] }
|
|
27
|
+
],
|
|
28
|
+
openapiConfig: {
|
|
29
|
+
info: {
|
|
30
|
+
title: "My API with Security",
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
description: "An example API with OpenAPI documentation, tags, and security"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
// Example routes with tags and security
|
|
38
|
+
app.get("/users", (ctx) => {
|
|
39
|
+
return { users: [] }
|
|
40
|
+
}, {
|
|
41
|
+
detail: {
|
|
42
|
+
tags: ["Users"],
|
|
43
|
+
summary: "Get all users",
|
|
44
|
+
description: "Retrieve a list of all users",
|
|
45
|
+
security: [{ bearerAuth: [] }]
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
app.get("/users/:id", (ctx) => {
|
|
50
|
+
const id = ctx.params.id
|
|
51
|
+
return { user: { id, name: "John Doe" } }
|
|
52
|
+
}, {
|
|
53
|
+
detail: {
|
|
54
|
+
tags: ["Users"],
|
|
55
|
+
summary: "Get user by ID",
|
|
56
|
+
description: "Retrieve a specific user by their ID",
|
|
57
|
+
params: {
|
|
58
|
+
id: z.string().describe("User ID")
|
|
59
|
+
},
|
|
60
|
+
security: [{ apiKeyAuth: [] }]
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
app.post("/users", (ctx) => {
|
|
65
|
+
const body = ctx.body
|
|
66
|
+
return { message: "User created", user: body }
|
|
67
|
+
}, {
|
|
68
|
+
body: z.object({
|
|
69
|
+
name: z.string(),
|
|
70
|
+
email: z.string().email()
|
|
71
|
+
}),
|
|
72
|
+
detail: {
|
|
73
|
+
tags: ["Users"],
|
|
74
|
+
summary: "Create a new user",
|
|
75
|
+
description: "Create a new user with the provided information",
|
|
76
|
+
security: [{ bearerAuth: [] }]
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
app.get("/products", (ctx) => {
|
|
81
|
+
return { products: [] }
|
|
82
|
+
}, {
|
|
83
|
+
detail: {
|
|
84
|
+
tags: ["Products"],
|
|
85
|
+
summary: "Get all products",
|
|
86
|
+
description: "Retrieve a list of all products"
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
app.get("/products/:id", (ctx) => {
|
|
91
|
+
const id = ctx.params.id
|
|
92
|
+
return { product: { id, name: "Sample Product" } }
|
|
93
|
+
}, {
|
|
94
|
+
detail: {
|
|
95
|
+
tags: ["Products"],
|
|
96
|
+
summary: "Get product by ID",
|
|
97
|
+
description: "Retrieve a specific product by its ID",
|
|
98
|
+
params: {
|
|
99
|
+
id: z.string().describe("Product ID")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Admin routes with different security
|
|
105
|
+
app.get("/admin/users", (ctx) => {
|
|
106
|
+
return { adminUsers: [] }
|
|
107
|
+
}, {
|
|
108
|
+
detail: {
|
|
109
|
+
tags: ["Admin"],
|
|
110
|
+
summary: "Get all users (Admin)",
|
|
111
|
+
description: "Admin-only endpoint to retrieve all users",
|
|
112
|
+
security: [{ bearerAuth: [] }, { apiKeyAuth: [] }]
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Health check route (no security required)
|
|
117
|
+
app.get("/health", (ctx) => {
|
|
118
|
+
return { status: "ok", timestamp: new Date().toISOString() }
|
|
119
|
+
}, {
|
|
120
|
+
detail: {
|
|
121
|
+
tags: ["System"],
|
|
122
|
+
summary: "Health check",
|
|
123
|
+
description: "Check if the API is running"
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Start the server
|
|
128
|
+
app.listen(3000, () => {
|
|
129
|
+
console.log("Server running on http://localhost:3000")
|
|
130
|
+
console.log("OpenAPI docs available at http://localhost:3000/docs")
|
|
131
|
+
console.log("OpenAPI JSON available at http://localhost:3000/openapi.json")
|
|
132
|
+
})
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bxo",
|
|
3
|
-
"module": "index.ts",
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"module": "./src/index.ts",
|
|
4
|
+
"exports": {
|
|
5
|
+
".": "./src/index.ts",
|
|
6
|
+
"./plugins": "./plugins/index.ts"
|
|
7
|
+
},
|
|
8
|
+
"version": "0.0.5-dev.71",
|
|
6
9
|
"type": "module",
|
|
7
10
|
"devDependencies": {
|
|
8
11
|
"@types/bun": "latest"
|
|
9
12
|
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": "./index.ts",
|
|
12
|
-
"./plugins": "./plugins/index.ts"
|
|
13
|
-
},
|
|
14
13
|
"peerDependencies": {
|
|
15
14
|
"typescript": "^5"
|
|
16
15
|
},
|
|
17
16
|
"dependencies": {
|
|
18
|
-
"zod": "^4.
|
|
17
|
+
"zod": "^4.1.5",
|
|
18
|
+
"zod-openapi": "^5.4.0"
|
|
19
19
|
}
|
|
20
20
|
}
|
package/plugins/cors.ts
CHANGED
|
@@ -1,83 +1,133 @@
|
|
|
1
|
-
import BXO from
|
|
2
|
-
|
|
3
|
-
interface
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import BXO from "../src/index";
|
|
2
|
+
|
|
3
|
+
export interface CorsOptions {
|
|
4
|
+
origin?: string | string[] | boolean | ((origin: string) => boolean);
|
|
5
|
+
methods?: string[];
|
|
6
|
+
allowedHeaders?: string[];
|
|
7
|
+
exposedHeaders?: string[];
|
|
8
|
+
credentials?: boolean;
|
|
9
|
+
maxAge?: number;
|
|
10
|
+
preflightContinue?: boolean;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
export function cors(options:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
export function cors(options: CorsOptions = {}): BXO {
|
|
14
|
+
const {
|
|
15
|
+
origin = "*",
|
|
16
|
+
methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"],
|
|
17
|
+
allowedHeaders = ["Content-Type", "Authorization"],
|
|
18
|
+
exposedHeaders = [],
|
|
19
|
+
credentials = false,
|
|
20
|
+
maxAge = 86400,
|
|
21
|
+
preflightContinue = false
|
|
22
|
+
} = options;
|
|
23
|
+
|
|
24
|
+
const plugin = new BXO();
|
|
25
|
+
|
|
26
|
+
// Handle CORS preflight requests
|
|
27
|
+
plugin.beforeRequest(async (req) => {
|
|
28
|
+
const requestOrigin = req.headers.get("origin");
|
|
29
|
+
const requestMethod = req.method;
|
|
30
|
+
|
|
31
|
+
// Handle preflight OPTIONS request
|
|
32
|
+
if (requestMethod === "OPTIONS") {
|
|
33
|
+
const response = new Response(null, { status: 204 });
|
|
34
|
+
|
|
35
|
+
// Set CORS headers
|
|
36
|
+
setCorsHeaders(response, {
|
|
37
|
+
origin,
|
|
38
|
+
methods,
|
|
39
|
+
allowedHeaders,
|
|
40
|
+
exposedHeaders,
|
|
41
|
+
credentials,
|
|
42
|
+
maxAge,
|
|
43
|
+
requestOrigin
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!preflightContinue) {
|
|
47
|
+
return response;
|
|
48
|
+
}
|
|
31
49
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
|
|
51
|
+
// Continue with normal request processing
|
|
52
|
+
return req;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Add CORS headers to all responses
|
|
56
|
+
plugin.afterRequest(async (req, res) => {
|
|
57
|
+
const requestOrigin = req.headers.get("origin");
|
|
58
|
+
|
|
59
|
+
// Set CORS headers on the response
|
|
60
|
+
setCorsHeaders(res, {
|
|
61
|
+
origin,
|
|
62
|
+
methods,
|
|
63
|
+
allowedHeaders,
|
|
64
|
+
exposedHeaders,
|
|
65
|
+
credentials,
|
|
66
|
+
maxAge,
|
|
67
|
+
requestOrigin
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return res;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return plugin;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setCorsHeaders(
|
|
77
|
+
response: Response,
|
|
78
|
+
options: {
|
|
79
|
+
origin: string | string[] | boolean | ((origin: string) => boolean);
|
|
80
|
+
methods: string[];
|
|
81
|
+
allowedHeaders: string[];
|
|
82
|
+
exposedHeaders: string[];
|
|
83
|
+
credentials: boolean;
|
|
84
|
+
maxAge: number;
|
|
85
|
+
requestOrigin?: string | null;
|
|
86
|
+
}
|
|
87
|
+
) {
|
|
88
|
+
const { origin, methods, allowedHeaders, exposedHeaders, credentials, maxAge, requestOrigin } = options;
|
|
89
|
+
|
|
90
|
+
// Set Access-Control-Allow-Origin
|
|
91
|
+
if (origin) {
|
|
92
|
+
if (typeof origin === "string") {
|
|
93
|
+
if (origin === "*") {
|
|
94
|
+
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
95
|
+
} else {
|
|
96
|
+
response.headers.set("Access-Control-Allow-Origin", origin);
|
|
97
|
+
}
|
|
98
|
+
} else if (Array.isArray(origin)) {
|
|
99
|
+
// For array of origins, we need to check if the request origin is in the list
|
|
100
|
+
// This is handled in the beforeRequest hook where we have access to the request origin
|
|
101
|
+
if (options.requestOrigin && origin.includes(options.requestOrigin)) {
|
|
102
|
+
response.headers.set("Access-Control-Allow-Origin", options.requestOrigin);
|
|
103
|
+
}
|
|
104
|
+
} else if (origin === true) {
|
|
105
|
+
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
38
106
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
headers['Access-Control-Allow-Methods'] = methods.join(', ');
|
|
42
|
-
headers['Access-Control-Allow-Headers'] = allowedHeaders.join(', ');
|
|
43
|
-
|
|
44
|
-
if (credentials) {
|
|
45
|
-
headers['Access-Control-Allow-Credentials'] = 'true';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
headers['Access-Control-Max-Age'] = maxAge.toString();
|
|
49
|
-
|
|
50
|
-
ctx.set.status = 204;
|
|
51
|
-
ctx.set.headers = { ...ctx.set.headers, ...headers };
|
|
52
|
-
|
|
53
|
-
throw new Response(null, { status: 204, headers });
|
|
54
107
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Handle origin for actual requests
|
|
61
|
-
if (typeof origin === 'boolean') {
|
|
62
|
-
if (origin) {
|
|
63
|
-
headers['Access-Control-Allow-Origin'] = ctx.request.headers.get('origin') || '*';
|
|
64
|
-
}
|
|
65
|
-
} else if (typeof origin === 'string') {
|
|
66
|
-
headers['Access-Control-Allow-Origin'] = origin;
|
|
67
|
-
} else if (Array.isArray(origin)) {
|
|
68
|
-
const requestOrigin = ctx.request.headers.get('origin');
|
|
69
|
-
if (requestOrigin && origin.includes(requestOrigin)) {
|
|
70
|
-
headers['Access-Control-Allow-Origin'] = requestOrigin;
|
|
71
|
-
}
|
|
108
|
+
|
|
109
|
+
// Set Access-Control-Allow-Methods
|
|
110
|
+
if (methods.length > 0) {
|
|
111
|
+
response.headers.set("Access-Control-Allow-Methods", methods.join(", "));
|
|
72
112
|
}
|
|
73
113
|
|
|
74
|
-
|
|
75
|
-
|
|
114
|
+
// Set Access-Control-Allow-Headers
|
|
115
|
+
if (allowedHeaders.length > 0) {
|
|
116
|
+
response.headers.set("Access-Control-Allow-Headers", allowedHeaders.join(", "));
|
|
76
117
|
}
|
|
77
118
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
119
|
+
// Set Access-Control-Expose-Headers
|
|
120
|
+
if (exposedHeaders.length > 0) {
|
|
121
|
+
response.headers.set("Access-Control-Expose-Headers", exposedHeaders.join(", "));
|
|
122
|
+
}
|
|
81
123
|
|
|
82
|
-
|
|
83
|
-
|
|
124
|
+
// Set Access-Control-Allow-Credentials
|
|
125
|
+
if (credentials) {
|
|
126
|
+
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Set Access-Control-Max-Age
|
|
130
|
+
if (maxAge) {
|
|
131
|
+
response.headers.set("Access-Control-Max-Age", maxAge.toString());
|
|
132
|
+
}
|
|
133
|
+
}
|
package/plugins/index.ts
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export { logger } from './logger';
|
|
4
|
-
export { auth, createJWT } from './auth';
|
|
5
|
-
export { rateLimit } from './ratelimit';
|
|
6
|
-
|
|
7
|
-
// Import BXO for plugin typing
|
|
8
|
-
import BXO from '../index';
|
|
9
|
-
|
|
10
|
-
// Plugin functions now return BXO instances
|
|
11
|
-
export type PluginFactory<T = any> = (options?: T) => BXO;
|
|
1
|
+
export * from "./openapi";
|
|
2
|
+
export * from "./cors";
|