bxo 0.0.5-dev.8 → 0.0.5-dev.80

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,151 @@
1
+ import BXO, { z } from "../src";
2
+
3
+ async function main() {
4
+ const app = new BXO({ serve: { port: 3001 } });
5
+
6
+ // Example 1: Simple cookie setting
7
+ app.get("/set-simple-cookie", (ctx) => {
8
+ // Set a simple cookie
9
+ ctx.set.cookie("theme", "dark");
10
+
11
+ return ctx.json({ message: "Simple cookie set!" });
12
+ });
13
+
14
+ // Example 2: Cookie with options
15
+ app.get("/set-secure-cookie", (ctx) => {
16
+ // Set a secure cookie with various options
17
+ ctx.set.cookie("sessionId", "abc123", {
18
+ httpOnly: true,
19
+ secure: true,
20
+ sameSite: "strict",
21
+ maxAge: 3600, // 1 hour
22
+ path: "/"
23
+ });
24
+
25
+ return ctx.json({ message: "Secure cookie set!" });
26
+ });
27
+
28
+ // Example 3: Multiple cookies
29
+ app.get("/set-multiple-cookies", (ctx) => {
30
+ // Set multiple cookies
31
+ ctx.set.cookie("user", "john_doe", {
32
+ httpOnly: true,
33
+ maxAge: 86400 // 1 day
34
+ });
35
+
36
+ ctx.set.cookie("preferences", "dark_mode", {
37
+ maxAge: 604800 // 1 week
38
+ });
39
+
40
+ ctx.set.cookie("lastVisit", new Date().toISOString(), {
41
+ expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days
42
+ });
43
+
44
+ return ctx.json({ message: "Multiple cookies set!" });
45
+ });
46
+
47
+ // Example 4: Reading cookies
48
+ app.get("/read-cookies", (ctx) => {
49
+ return ctx.json({
50
+ message: "Current cookies:",
51
+ cookies: ctx.cookies,
52
+ theme: ctx.cookies.theme || "not set",
53
+ sessionId: ctx.cookies.sessionId || "not set",
54
+ user: ctx.cookies.user || "not set"
55
+ });
56
+ });
57
+
58
+ // Example 5: Login with cookie validation
59
+ app.post("/login", (ctx) => {
60
+ const { username, password } = ctx.body;
61
+
62
+ // Simple validation (in real app, check against database)
63
+ if (username === "admin" && password === "password") {
64
+ // Set session cookie
65
+ ctx.set.cookie("sessionId", `session_${Date.now()}`, {
66
+ httpOnly: true,
67
+ secure: process.env.NODE_ENV === "production",
68
+ sameSite: "strict",
69
+ maxAge: 3600 // 1 hour
70
+ });
71
+
72
+ ctx.set.cookie("username", username, {
73
+ maxAge: 3600
74
+ });
75
+
76
+ return ctx.json({
77
+ message: "Login successful!",
78
+ username
79
+ });
80
+ }
81
+
82
+ return ctx.status(401, { error: "Invalid credentials" });
83
+ }, {
84
+ body: z.object({
85
+ username: z.string(),
86
+ password: z.string()
87
+ })
88
+ });
89
+
90
+ // Example 6: Protected route with cookie validation
91
+ app.get("/profile", (ctx) => {
92
+ const sessionId = ctx.cookies.sessionId;
93
+ const username = ctx.cookies.username;
94
+
95
+ if (!sessionId) {
96
+ return ctx.status(401, { error: "Not authenticated" });
97
+ }
98
+
99
+ return ctx.json({
100
+ message: "Welcome to your profile!",
101
+ username,
102
+ sessionId: sessionId.substring(0, 10) + "..." // Don't expose full session ID
103
+ });
104
+ }, {
105
+ cookies: z.object({
106
+ sessionId: z.string().optional(),
107
+ username: z.string().optional()
108
+ })
109
+ });
110
+
111
+ // Example 7: Logout (clear cookies)
112
+ app.post("/logout", (ctx) => {
113
+ // Clear cookies by setting them with maxAge: 0
114
+ ctx.set.cookie("sessionId", "", {
115
+ maxAge: 0,
116
+ path: "/"
117
+ });
118
+
119
+ ctx.set.cookie("username", "", {
120
+ maxAge: 0,
121
+ path: "/"
122
+ });
123
+
124
+ return ctx.json({ message: "Logged out successfully" });
125
+ });
126
+
127
+ // Example 8: Cookie with domain and path
128
+ app.get("/set-domain-cookie", (ctx) => {
129
+ ctx.set.cookie("globalPref", "enabled", {
130
+ domain: "localhost", // or your domain
131
+ path: "/api",
132
+ maxAge: 86400
133
+ });
134
+
135
+ return ctx.json({ message: "Domain-specific cookie set!" });
136
+ });
137
+
138
+ app.start();
139
+ console.log(`Cookie example server running on http://localhost:${app.server?.port}`);
140
+ console.log("\nTry these endpoints:");
141
+ console.log("GET /set-simple-cookie");
142
+ console.log("GET /set-secure-cookie");
143
+ console.log("GET /set-multiple-cookies");
144
+ console.log("GET /read-cookies");
145
+ console.log("POST /login (with body: {\"username\": \"admin\", \"password\": \"password\"})");
146
+ console.log("GET /profile");
147
+ console.log("POST /logout");
148
+ console.log("GET /set-domain-cookie");
149
+ }
150
+
151
+ main().catch(console.error);
@@ -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!");
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body>
3
+ <h1>Hello, world!</h1>
4
+ </body>
5
+ </html>
@@ -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({ serve: { port: 0 } });
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,203 @@
1
+ import BXO from "../src/index";
2
+ import { z } from "zod";
3
+
4
+ const app = new BXO();
5
+
6
+ // Define Zod schema for the form data structure
7
+ const UserFormSchema = z.object({
8
+ name: z.string(),
9
+ email: z.string().email(),
10
+ password: z.string().min(6),
11
+ is_active: z.string().transform(val => val === "1"),
12
+ profile: z.object({
13
+ name: z.string()
14
+ }),
15
+ id: z.string().optional(),
16
+ created_at: z.string().optional(),
17
+ updated_at: z.string().optional(),
18
+ idx: z.string().transform(val => parseInt(val, 10))
19
+ });
20
+
21
+ // Route to handle multipart/form-data with nested objects
22
+ app.post("/users", async (ctx) => {
23
+ // The form data is automatically parsed into nested objects
24
+ // ctx.body will contain the structured data based on the schema
25
+ console.log("Parsed form data:", ctx.body);
26
+
27
+ return ctx.json({
28
+ message: "User created successfully",
29
+ data: ctx.body
30
+ });
31
+ }, {
32
+ body: UserFormSchema,
33
+ detail: {
34
+ summary: "Create user with multipart/form-data",
35
+ description: "Handles form data with nested objects and arrays",
36
+ tags: ["Users"]
37
+ }
38
+ });
39
+
40
+ // Example with arrays
41
+ const ArrayFormSchema = z.object({
42
+ items: z.array(z.string()),
43
+ tags: z.array(z.string()),
44
+ profile: z.object({
45
+ name: z.string(),
46
+ age: z.string().transform(val => parseInt(val, 10))
47
+ })
48
+ });
49
+
50
+ app.post("/items", async (ctx) => {
51
+ console.log("Parsed array form data:", ctx.body);
52
+
53
+ return ctx.json({
54
+ message: "Items processed successfully",
55
+ data: ctx.body
56
+ });
57
+ }, {
58
+ body: ArrayFormSchema,
59
+ detail: {
60
+ summary: "Process items with arrays",
61
+ description: "Handles form data with arrays like items[0], items[1]",
62
+ tags: ["Items"]
63
+ }
64
+ });
65
+
66
+ // Test route to show how the parsing works
67
+ app.get("/test-parsing", async (ctx) => {
68
+ const html = `
69
+ <!DOCTYPE html>
70
+ <html>
71
+ <head>
72
+ <title>Multipart Form Data Test</title>
73
+ <style>
74
+ body { font-family: Arial, sans-serif; margin: 40px; }
75
+ .form-group { margin: 20px 0; }
76
+ label { display: block; margin-bottom: 5px; font-weight: bold; }
77
+ input, textarea { width: 300px; padding: 8px; margin-bottom: 10px; }
78
+ button { background: #007bff; color: white; padding: 10px 20px; border: none; cursor: pointer; }
79
+ .result { margin-top: 20px; padding: 20px; background: #f8f9fa; border-radius: 5px; }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <h1>Multipart Form Data Parsing Test</h1>
84
+
85
+ <h2>Test 1: Nested Objects (like your image example)</h2>
86
+ <form id="userForm" enctype="multipart/form-data">
87
+ <div class="form-group">
88
+ <label>Name:</label>
89
+ <input type="text" name="name" value="John Doe" required>
90
+ </div>
91
+ <div class="form-group">
92
+ <label>Email:</label>
93
+ <input type="email" name="email" value="john@example.com" required>
94
+ </div>
95
+ <div class="form-group">
96
+ <label>Password:</label>
97
+ <input type="password" name="password" value="password123" required>
98
+ </div>
99
+ <div class="form-group">
100
+ <label>Is Active:</label>
101
+ <input type="text" name="is_active" value="1">
102
+ </div>
103
+ <div class="form-group">
104
+ <label>Profile Name:</label>
105
+ <input type="text" name="profile[name]" value="John Profile" required>
106
+ </div>
107
+ <div class="form-group">
108
+ <label>ID:</label>
109
+ <input type="text" name="id" value="UUID()">
110
+ </div>
111
+ <div class="form-group">
112
+ <label>Created At:</label>
113
+ <input type="text" name="created_at" value="NOW()">
114
+ </div>
115
+ <div class="form-group">
116
+ <label>Updated At:</label>
117
+ <input type="text" name="updated_at" value="NOW()">
118
+ </div>
119
+ <div class="form-group">
120
+ <label>Index:</label>
121
+ <input type="text" name="idx" value="0">
122
+ </div>
123
+ <button type="submit">Submit User Form</button>
124
+ </form>
125
+
126
+ <h2>Test 2: Arrays</h2>
127
+ <form id="itemsForm" enctype="multipart/form-data">
128
+ <div class="form-group">
129
+ <label>Item 1:</label>
130
+ <input type="text" name="items[0]" value="Apple">
131
+ </div>
132
+ <div class="form-group">
133
+ <label>Item 2:</label>
134
+ <input type="text" name="items[1]" value="Banana">
135
+ </div>
136
+ <div class="form-group">
137
+ <label>Item 3:</label>
138
+ <input type="text" name="items[2]" value="Cherry">
139
+ </div>
140
+ <div class="form-group">
141
+ <label>Tag 1:</label>
142
+ <input type="text" name="tags[0]" value="fruit">
143
+ </div>
144
+ <div class="form-group">
145
+ <label>Tag 2:</label>
146
+ <input type="text" name="tags[1]" value="healthy">
147
+ </div>
148
+ <div class="form-group">
149
+ <label>Profile Name:</label>
150
+ <input type="text" name="profile[name]" value="Test Profile">
151
+ </div>
152
+ <div class="form-group">
153
+ <label>Profile Age:</label>
154
+ <input type="text" name="profile[age]" value="25">
155
+ </div>
156
+ <button type="submit">Submit Items Form</button>
157
+ </form>
158
+
159
+ <div id="result" class="result" style="display: none;">
160
+ <h3>Result:</h3>
161
+ <pre id="resultContent"></pre>
162
+ </div>
163
+
164
+ <script>
165
+ async function submitForm(form, endpoint) {
166
+ const formData = new FormData(form);
167
+ try {
168
+ const response = await fetch(endpoint, {
169
+ method: 'POST',
170
+ body: formData
171
+ });
172
+ const result = await response.json();
173
+ document.getElementById('result').style.display = 'block';
174
+ document.getElementById('resultContent').textContent = JSON.stringify(result, null, 2);
175
+ } catch (error) {
176
+ document.getElementById('result').style.display = 'block';
177
+ document.getElementById('resultContent').textContent = 'Error: ' + error.message;
178
+ }
179
+ }
180
+
181
+ document.getElementById('userForm').addEventListener('submit', (e) => {
182
+ e.preventDefault();
183
+ submitForm(e.target, '/users');
184
+ });
185
+
186
+ document.getElementById('itemsForm').addEventListener('submit', (e) => {
187
+ e.preventDefault();
188
+ submitForm(e.target, '/items');
189
+ });
190
+ </script>
191
+ </body>
192
+ </html>
193
+ `;
194
+
195
+ return new Response(html, {
196
+ headers: { "Content-Type": "text/html" }
197
+ });
198
+ });
199
+
200
+ app.start();
201
+
202
+ console.log("🚀 Server running at http://localhost:3000");
203
+ console.log("📝 Test the multipart parsing at http://localhost:3000/test-parsing");