princejs 1.7.0 → 1.7.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.
- package/Readme.md +36 -126
- package/dist/helpers.d.ts +1 -13
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +23 -20
- package/dist/jsx.d.ts +16 -0
- package/dist/jsx.d.ts.map +1 -0
- package/dist/jsx.js +66 -0
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +12545 -69
- package/dist/prince.d.ts +11 -18
- package/dist/prince.d.ts.map +1 -1
- package/dist/prince.js +228 -208
- package/package.json +9 -3
package/dist/prince.d.ts
CHANGED
|
@@ -2,22 +2,13 @@ type Next = () => Promise<Response>;
|
|
|
2
2
|
type Middleware = (req: PrinceRequest, next: Next) => Promise<Response | undefined> | Response | undefined;
|
|
3
3
|
type HandlerResult = Response | Record<string, any> | string | Uint8Array;
|
|
4
4
|
export interface PrinceRequest extends Request {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
text(): Promise<string>;
|
|
8
|
-
formData(): Promise<FormData>;
|
|
9
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
|
5
|
+
parsedBody?: any;
|
|
6
|
+
files?: Record<string, File>;
|
|
10
7
|
user?: any;
|
|
11
8
|
params?: Record<string, string>;
|
|
12
9
|
query?: URLSearchParams;
|
|
13
10
|
[key: string]: any;
|
|
14
11
|
}
|
|
15
|
-
export interface WebSocketHandler {
|
|
16
|
-
open?: (ws: any) => void;
|
|
17
|
-
message?: (ws: any, msg: string | Buffer) => void;
|
|
18
|
-
close?: (ws: any, code?: number, reason?: string) => void;
|
|
19
|
-
drain?: (ws: any) => void;
|
|
20
|
-
}
|
|
21
12
|
type RouteHandler = (req: PrinceRequest) => Promise<HandlerResult> | HandlerResult;
|
|
22
13
|
declare class ResponseBuilder {
|
|
23
14
|
private _status;
|
|
@@ -29,7 +20,6 @@ declare class ResponseBuilder {
|
|
|
29
20
|
text(data: string): Response;
|
|
30
21
|
html(data: string): Response;
|
|
31
22
|
redirect(url: string, status?: number): Response;
|
|
32
|
-
stream(cb: (push: (chunk: string) => void, close: () => void) => void): Response;
|
|
33
23
|
build(): Response;
|
|
34
24
|
}
|
|
35
25
|
export declare class Prince {
|
|
@@ -40,24 +30,27 @@ export declare class Prince {
|
|
|
40
30
|
private wsRoutes;
|
|
41
31
|
private openapiData;
|
|
42
32
|
private router;
|
|
33
|
+
private staticRoutes;
|
|
34
|
+
private routeCache;
|
|
43
35
|
constructor(devMode?: boolean);
|
|
44
36
|
use(mw: Middleware): this;
|
|
45
37
|
error(fn: (err: any, req: PrinceRequest) => Response): this;
|
|
46
38
|
json(data: any, status?: number): Response;
|
|
47
39
|
response(): ResponseBuilder;
|
|
48
|
-
ws(path: string, options: Partial<WebSocketHandler>): this;
|
|
49
|
-
openapi(path?: string): this;
|
|
50
40
|
get(path: string, handler: RouteHandler): this;
|
|
51
41
|
post(path: string, handler: RouteHandler): this;
|
|
52
42
|
put(path: string, handler: RouteHandler): this;
|
|
53
43
|
delete(path: string, handler: RouteHandler): this;
|
|
54
44
|
patch(path: string, handler: RouteHandler): this;
|
|
45
|
+
options(path: string, handler: RouteHandler): this;
|
|
55
46
|
private add;
|
|
56
|
-
private isWildcard;
|
|
57
|
-
private parseUrl;
|
|
58
|
-
private parseBody;
|
|
59
47
|
private buildRouter;
|
|
60
|
-
private
|
|
48
|
+
private insertRoute;
|
|
49
|
+
private findRoute;
|
|
50
|
+
private matchPath;
|
|
51
|
+
private matchRoute;
|
|
52
|
+
private parseBody;
|
|
53
|
+
private executeHandler;
|
|
61
54
|
handleFetch(req: Request): Promise<Response>;
|
|
62
55
|
fetch(req: Request): Promise<Response>;
|
|
63
56
|
listen(port?: number): void;
|
package/dist/prince.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prince.d.ts","sourceRoot":"","sources":["../src/prince.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prince.d.ts","sourceRoot":"","sources":["../src/prince.ts"],"names":[],"mappings":"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"}
|
package/dist/prince.js
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/prince.ts
|
|
3
|
-
class TrieNode {
|
|
4
|
-
children = Object.create(null);
|
|
5
|
-
paramChild;
|
|
6
|
-
wildcardChild;
|
|
7
|
-
catchAllChild;
|
|
8
|
-
handlers = null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
3
|
class ResponseBuilder {
|
|
12
4
|
_status = 200;
|
|
13
5
|
_headers = {};
|
|
@@ -40,15 +32,6 @@ class ResponseBuilder {
|
|
|
40
32
|
this._headers["Location"] = url;
|
|
41
33
|
return this.build();
|
|
42
34
|
}
|
|
43
|
-
stream(cb) {
|
|
44
|
-
const encoder = new TextEncoder;
|
|
45
|
-
const stream = new ReadableStream({
|
|
46
|
-
start(controller) {
|
|
47
|
-
cb((chunk) => controller.enqueue(encoder.encode(chunk)), () => controller.close());
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
return new Response(stream, { status: this._status, headers: this._headers });
|
|
51
|
-
}
|
|
52
35
|
build() {
|
|
53
36
|
return new Response(this._body, { status: this._status, headers: this._headers });
|
|
54
37
|
}
|
|
@@ -62,6 +45,8 @@ class Prince {
|
|
|
62
45
|
wsRoutes = {};
|
|
63
46
|
openapiData = null;
|
|
64
47
|
router = null;
|
|
48
|
+
staticRoutes = new Map;
|
|
49
|
+
routeCache = new Map;
|
|
65
50
|
constructor(devMode = false) {
|
|
66
51
|
this.devMode = devMode;
|
|
67
52
|
}
|
|
@@ -82,27 +67,6 @@ class Prince {
|
|
|
82
67
|
response() {
|
|
83
68
|
return new ResponseBuilder;
|
|
84
69
|
}
|
|
85
|
-
ws(path, options) {
|
|
86
|
-
this.wsRoutes[path] = options;
|
|
87
|
-
return this;
|
|
88
|
-
}
|
|
89
|
-
openapi(path = "/docs") {
|
|
90
|
-
const paths = {};
|
|
91
|
-
for (const route of this.rawRoutes) {
|
|
92
|
-
paths[route.path] ??= {};
|
|
93
|
-
paths[route.path][route.method.toLowerCase()] = {
|
|
94
|
-
summary: "",
|
|
95
|
-
responses: { 200: { description: "OK" } }
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
this.openapiData = {
|
|
99
|
-
openapi: "3.1.0",
|
|
100
|
-
info: { title: "PrinceJS API", version: "1.0.0" },
|
|
101
|
-
paths
|
|
102
|
-
};
|
|
103
|
-
this.get(path, () => this.openapiData);
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
70
|
get(path, handler) {
|
|
107
71
|
return this.add("GET", path, handler);
|
|
108
72
|
}
|
|
@@ -118,6 +82,9 @@ class Prince {
|
|
|
118
82
|
patch(path, handler) {
|
|
119
83
|
return this.add("PATCH", path, handler);
|
|
120
84
|
}
|
|
85
|
+
options(path, handler) {
|
|
86
|
+
return this.add("OPTIONS", path, handler);
|
|
87
|
+
}
|
|
121
88
|
add(method, path, handler) {
|
|
122
89
|
if (!path.startsWith("/"))
|
|
123
90
|
path = "/" + path;
|
|
@@ -125,175 +92,257 @@ class Prince {
|
|
|
125
92
|
path = path.slice(0, -1);
|
|
126
93
|
const parts = path === "/" ? [""] : path.split("/").slice(1);
|
|
127
94
|
this.rawRoutes.push({ method, path, parts, handler });
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
parts,
|
|
133
|
-
handler: () => new Response(null, { status: 204 })
|
|
134
|
-
});
|
|
95
|
+
const isStaticRoute = !parts.some((part) => part.includes(":") || part.includes("*") || part.includes("("));
|
|
96
|
+
if (isStaticRoute) {
|
|
97
|
+
const staticKey = `${method}:${path}`;
|
|
98
|
+
this.staticRoutes.set(staticKey, handler);
|
|
135
99
|
}
|
|
100
|
+
this.routeCache.clear();
|
|
136
101
|
this.router = null;
|
|
137
102
|
return this;
|
|
138
103
|
}
|
|
139
|
-
|
|
140
|
-
|
|
104
|
+
buildRouter() {
|
|
105
|
+
if (this.router)
|
|
106
|
+
return this.router;
|
|
107
|
+
const root = {
|
|
108
|
+
pattern: "",
|
|
109
|
+
handlers: {},
|
|
110
|
+
children: []
|
|
111
|
+
};
|
|
112
|
+
for (const route of this.rawRoutes) {
|
|
113
|
+
if (this.staticRoutes.has(`${route.method}:${route.path}`)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
this.insertRoute(root, route);
|
|
117
|
+
}
|
|
118
|
+
this.router = root;
|
|
119
|
+
return root;
|
|
141
120
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
121
|
+
insertRoute(node, route) {
|
|
122
|
+
let currentNode = node;
|
|
123
|
+
for (let i = 0;i < route.parts.length; i++) {
|
|
124
|
+
const part = route.parts[i];
|
|
125
|
+
let found = false;
|
|
126
|
+
for (const child of currentNode.children) {
|
|
127
|
+
if (child.pattern === part) {
|
|
128
|
+
currentNode = child;
|
|
129
|
+
found = true;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!found) {
|
|
134
|
+
const newNode = {
|
|
135
|
+
pattern: part,
|
|
136
|
+
handlers: {},
|
|
137
|
+
children: []
|
|
138
|
+
};
|
|
139
|
+
if (part.startsWith(":")) {
|
|
140
|
+
newNode.paramName = part.slice(1);
|
|
141
|
+
} else if (part === "*") {
|
|
142
|
+
newNode.isWildcard = true;
|
|
143
|
+
} else if (part === "**") {
|
|
144
|
+
newNode.isCatchAll = true;
|
|
145
|
+
}
|
|
146
|
+
currentNode.children.push(newNode);
|
|
147
|
+
currentNode = newNode;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
currentNode.handlers[route.method] = route.handler;
|
|
148
151
|
}
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
152
|
+
findRoute(method, pathname) {
|
|
153
|
+
const cacheKey = `${method}:${pathname}`;
|
|
154
|
+
if (this.routeCache.has(cacheKey)) {
|
|
155
|
+
return this.routeCache.get(cacheKey);
|
|
156
|
+
}
|
|
157
|
+
const staticKey = `${method}:${pathname}`;
|
|
158
|
+
const staticHandler = this.staticRoutes.get(staticKey);
|
|
159
|
+
if (staticHandler) {
|
|
160
|
+
const result2 = { handler: staticHandler, params: {} };
|
|
161
|
+
this.routeCache.set(cacheKey, result2);
|
|
162
|
+
return result2;
|
|
163
|
+
}
|
|
164
|
+
const allowedMethods = new Set;
|
|
165
|
+
let pathExists = false;
|
|
166
|
+
for (const route of this.rawRoutes) {
|
|
167
|
+
if (this.matchPath(route.path, pathname)) {
|
|
168
|
+
pathExists = true;
|
|
169
|
+
allowedMethods.add(route.method);
|
|
164
170
|
}
|
|
165
|
-
return { files, fields };
|
|
166
171
|
}
|
|
167
|
-
if (
|
|
168
|
-
|
|
172
|
+
if (!pathExists) {
|
|
173
|
+
this.routeCache.set(cacheKey, null);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const segments = pathname === "/" ? [""] : pathname.split("/").slice(1);
|
|
177
|
+
const result = this.matchRoute(this.buildRouter(), segments, method);
|
|
178
|
+
if (result) {
|
|
179
|
+
this.routeCache.set(cacheKey, result);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
if (pathExists) {
|
|
183
|
+
const methodNotAllowed = {
|
|
184
|
+
handler: null,
|
|
185
|
+
params: {},
|
|
186
|
+
allowedMethods: Array.from(allowedMethods)
|
|
187
|
+
};
|
|
188
|
+
this.routeCache.set(cacheKey, methodNotAllowed);
|
|
189
|
+
return methodNotAllowed;
|
|
190
|
+
}
|
|
191
|
+
this.routeCache.set(cacheKey, null);
|
|
169
192
|
return null;
|
|
170
193
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (aHasParam && !bHasParam)
|
|
185
|
-
return 1;
|
|
186
|
-
if (!aHasParam && bHasParam)
|
|
187
|
-
return -1;
|
|
188
|
-
return 0;
|
|
189
|
-
});
|
|
190
|
-
for (const r of sortedRoutes) {
|
|
191
|
-
let node = root;
|
|
192
|
-
if (r.parts.length === 1 && r.parts[0] === "") {
|
|
193
|
-
node.handlers ??= {};
|
|
194
|
-
node.handlers[r.method] = r.handler;
|
|
194
|
+
matchPath(routePath, requestPath) {
|
|
195
|
+
const routeParts = routePath === "/" ? [""] : routePath.split("/").slice(1);
|
|
196
|
+
const requestParts = requestPath === "/" ? [""] : requestPath.split("/").slice(1);
|
|
197
|
+
if (routeParts.length !== requestParts.length) {
|
|
198
|
+
if (routeParts.includes("**")) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
for (let i = 0;i < routeParts.length; i++) {
|
|
204
|
+
const routePart = routeParts[i];
|
|
205
|
+
const requestPart = requestParts[i];
|
|
206
|
+
if (routePart.startsWith(":") || routePart === "*" || routePart === "**") {
|
|
195
207
|
continue;
|
|
196
208
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
if (routePart !== requestPart) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
matchRoute(node, segments, method, params = {}, index = 0) {
|
|
216
|
+
if (index === segments.length) {
|
|
217
|
+
const handler = node.handlers[method];
|
|
218
|
+
return handler ? { handler, params } : null;
|
|
219
|
+
}
|
|
220
|
+
const segment = segments[index];
|
|
221
|
+
for (const child of node.children) {
|
|
222
|
+
if (!child.paramName && !child.isWildcard && !child.isCatchAll) {
|
|
223
|
+
if (child.pattern === segment) {
|
|
224
|
+
const result = this.matchRoute(child, segments, method, params, index + 1);
|
|
225
|
+
if (result)
|
|
226
|
+
return result;
|
|
212
227
|
}
|
|
213
228
|
}
|
|
214
|
-
node.handlers ??= {};
|
|
215
|
-
node.handlers[r.method] = r.handler;
|
|
216
229
|
}
|
|
217
|
-
|
|
218
|
-
|
|
230
|
+
for (const child of node.children) {
|
|
231
|
+
if (child.paramName) {
|
|
232
|
+
params[child.paramName] = segment;
|
|
233
|
+
const result = this.matchRoute(child, segments, method, params, index + 1);
|
|
234
|
+
if (result)
|
|
235
|
+
return result;
|
|
236
|
+
delete params[child.paramName];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const child of node.children) {
|
|
240
|
+
if (child.isWildcard) {
|
|
241
|
+
const result = this.matchRoute(child, segments, method, params, index + 1);
|
|
242
|
+
if (result)
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const child of node.children) {
|
|
247
|
+
if (child.isCatchAll) {
|
|
248
|
+
const handler = child.handlers[method];
|
|
249
|
+
if (handler) {
|
|
250
|
+
return { handler, params };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
219
255
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
async parseBody(req) {
|
|
257
|
+
const ct = req.headers.get("content-type") || "";
|
|
258
|
+
const clonedReq = req.clone();
|
|
259
|
+
try {
|
|
260
|
+
if (ct.includes("application/json")) {
|
|
261
|
+
return await clonedReq.json();
|
|
262
|
+
}
|
|
263
|
+
if (ct.includes("application/x-www-form-urlencoded")) {
|
|
264
|
+
const text = await clonedReq.text();
|
|
265
|
+
return Object.fromEntries(new URLSearchParams(text));
|
|
266
|
+
}
|
|
267
|
+
if (ct.startsWith("multipart/form-data")) {
|
|
268
|
+
const fd = await clonedReq.formData();
|
|
269
|
+
const files = {};
|
|
270
|
+
const fields = {};
|
|
271
|
+
for (const [k, v] of fd.entries()) {
|
|
272
|
+
if (v instanceof File)
|
|
273
|
+
files[k] = v;
|
|
274
|
+
else
|
|
275
|
+
fields[k] = v;
|
|
276
|
+
}
|
|
277
|
+
return { files, fields };
|
|
278
|
+
}
|
|
279
|
+
if (ct.startsWith("text/")) {
|
|
280
|
+
return await clonedReq.text();
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error("Body parsing error:", error);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
async executeHandler(req, handler, params, query) {
|
|
289
|
+
Object.defineProperty(req, "params", { value: params, writable: true, configurable: true });
|
|
290
|
+
Object.defineProperty(req, "query", { value: query, writable: true, configurable: true });
|
|
291
|
+
if (["POST", "PUT", "PATCH"].includes(req.method)) {
|
|
292
|
+
const parsed = await this.parseBody(req);
|
|
293
|
+
if (parsed) {
|
|
294
|
+
if (typeof parsed === "object" && "files" in parsed && "fields" in parsed) {
|
|
295
|
+
Object.defineProperty(req, "parsedBody", { value: parsed.fields, writable: true, configurable: true });
|
|
228
296
|
Object.defineProperty(req, "files", { value: parsed.files, writable: true, configurable: true });
|
|
229
297
|
} else {
|
|
230
|
-
Object.defineProperty(req, "
|
|
298
|
+
Object.defineProperty(req, "parsedBody", { value: parsed, writable: true, configurable: true });
|
|
231
299
|
}
|
|
232
300
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
return
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
301
|
+
}
|
|
302
|
+
Object.defineProperty(req, "body", {
|
|
303
|
+
get: () => req.parsedBody,
|
|
304
|
+
set: (value) => {
|
|
305
|
+
req.parsedBody = value;
|
|
306
|
+
},
|
|
307
|
+
configurable: true
|
|
308
|
+
});
|
|
309
|
+
let i = 0;
|
|
310
|
+
const next = async () => {
|
|
311
|
+
while (i < this.middlewares.length) {
|
|
312
|
+
const result = await this.middlewares[i++](req, next);
|
|
313
|
+
if (result instanceof Response)
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
const res = await handler(req);
|
|
317
|
+
if (res instanceof Response)
|
|
318
|
+
return res;
|
|
319
|
+
if (typeof res === "string")
|
|
320
|
+
return new Response(res);
|
|
321
|
+
if (res instanceof Uint8Array)
|
|
322
|
+
return new Response(res);
|
|
323
|
+
return this.json(res);
|
|
250
324
|
};
|
|
325
|
+
return next();
|
|
251
326
|
}
|
|
252
327
|
async handleFetch(req) {
|
|
253
328
|
const url = new URL(req.url);
|
|
254
329
|
const r = req;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
330
|
+
const method = req.method;
|
|
331
|
+
const pathname = url.pathname;
|
|
332
|
+
const routeMatch = this.findRoute(method, pathname);
|
|
333
|
+
if (!routeMatch) {
|
|
334
|
+
return this.json({ error: "Not Found" }, 404);
|
|
335
|
+
}
|
|
336
|
+
if (routeMatch.allowedMethods && !routeMatch.handler) {
|
|
337
|
+
return new Response(JSON.stringify({ error: "Method Not Allowed" }), {
|
|
338
|
+
status: 405,
|
|
258
339
|
headers: {
|
|
259
|
-
|
|
260
|
-
"
|
|
261
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
262
|
-
"Access-Control-Max-Age": "86400"
|
|
340
|
+
Allow: routeMatch.allowedMethods.join(", "),
|
|
341
|
+
"Content-Type": "application/json"
|
|
263
342
|
}
|
|
264
343
|
});
|
|
265
344
|
}
|
|
266
|
-
|
|
267
|
-
value: url.searchParams,
|
|
268
|
-
writable: true,
|
|
269
|
-
configurable: true
|
|
270
|
-
});
|
|
271
|
-
const pathname = url.pathname;
|
|
272
|
-
const segments = pathname === "/" ? [] : pathname.slice(1).split("/");
|
|
273
|
-
const router = this.buildRouter();
|
|
274
|
-
let node = router;
|
|
275
|
-
let params = {};
|
|
276
|
-
for (let i = 0;i < segments.length; i++) {
|
|
277
|
-
const seg = segments[i];
|
|
278
|
-
if (node.children[seg]) {
|
|
279
|
-
node = node.children[seg];
|
|
280
|
-
} else if (node.paramChild) {
|
|
281
|
-
params[node.paramChild.name] = seg;
|
|
282
|
-
node = node.paramChild.node;
|
|
283
|
-
} else if (node.wildcardChild) {
|
|
284
|
-
node = node.wildcardChild;
|
|
285
|
-
} else if (node.catchAllChild) {
|
|
286
|
-
node = node.catchAllChild.node;
|
|
287
|
-
break;
|
|
288
|
-
} else {
|
|
289
|
-
return this.json({ error: "Not Found" }, 404);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const handler = node.handlers?.[req.method];
|
|
293
|
-
if (!handler)
|
|
294
|
-
return this.json({ error: "Method Not Allowed" }, 405);
|
|
295
|
-
const pipeline = this.compilePipeline(handler);
|
|
296
|
-
return pipeline(r, params, new URLSearchParams(url.search));
|
|
345
|
+
return this.executeHandler(r, routeMatch.handler, routeMatch.params, url.searchParams);
|
|
297
346
|
}
|
|
298
347
|
async fetch(req) {
|
|
299
348
|
try {
|
|
@@ -312,36 +361,7 @@ class Prince {
|
|
|
312
361
|
const self = this;
|
|
313
362
|
Bun.serve({
|
|
314
363
|
port,
|
|
315
|
-
fetch(req, server)
|
|
316
|
-
const { pathname } = new URL(req.url);
|
|
317
|
-
const ws = self.wsRoutes[pathname];
|
|
318
|
-
if (ws && server.upgrade(req, { data: { ws } })) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
return self.handleFetch(req).catch((err) => {
|
|
322
|
-
if (self.errorHandler)
|
|
323
|
-
return self.errorHandler(err, req);
|
|
324
|
-
if (self.devMode) {
|
|
325
|
-
console.error("Error:", err);
|
|
326
|
-
return self.json({ error: String(err), stack: err.stack }, 500);
|
|
327
|
-
}
|
|
328
|
-
return self.json({ error: "Internal Server Error" }, 500);
|
|
329
|
-
});
|
|
330
|
-
},
|
|
331
|
-
websocket: {
|
|
332
|
-
open(ws) {
|
|
333
|
-
ws.data?.ws?.open?.(ws);
|
|
334
|
-
},
|
|
335
|
-
message(ws, msg) {
|
|
336
|
-
ws.data?.ws?.message?.(ws, msg);
|
|
337
|
-
},
|
|
338
|
-
close(ws, code, reason) {
|
|
339
|
-
ws.data?.ws?.close?.(ws, code, reason);
|
|
340
|
-
},
|
|
341
|
-
drain(ws) {
|
|
342
|
-
ws.data?.ws?.drain?.(ws);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
364
|
+
fetch: (req, server) => self.fetch(req)
|
|
345
365
|
});
|
|
346
366
|
console.log(`\uD83D\uDE80 PrinceJS running on http://localhost:${port}`);
|
|
347
367
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "princejs",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "An easy and fast backend framework — by a 13yo developer, for developers.",
|
|
5
5
|
"main": "dist/prince.js",
|
|
6
6
|
"types": "dist/prince.d.ts",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"./scheduler": {
|
|
25
25
|
"import": "./dist/scheduler.js",
|
|
26
26
|
"types": "./dist/scheduler.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./jsx": {
|
|
29
|
+
"import": "./dist/jsx.js",
|
|
30
|
+
"types": "./dist/jsx.d.ts"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"files": [
|
|
@@ -44,7 +48,9 @@
|
|
|
44
48
|
"websocket",
|
|
45
49
|
"middleware",
|
|
46
50
|
"scheduler",
|
|
47
|
-
"fast"
|
|
51
|
+
"fast",
|
|
52
|
+
"lightweight",
|
|
53
|
+
"ssr"
|
|
48
54
|
],
|
|
49
55
|
"author": "Matthew Michael (MatthewTheCoder1218)",
|
|
50
56
|
"license": "MIT",
|
|
@@ -80,7 +86,7 @@
|
|
|
80
86
|
"jose": "^6.1.2"
|
|
81
87
|
},
|
|
82
88
|
"scripts": {
|
|
83
|
-
"build:js": "bun build src/prince.ts --outdir dist --target bun && bun build src/middleware.ts --outdir dist --target bun && bun build src/helpers.ts --outdir dist --target bun && bun build src/scheduler.ts --outdir dist --target bun && bun build bin/create.ts --outdir dist --target bun --format esm",
|
|
89
|
+
"build:js": "bun build src/prince.ts --outdir dist --target bun && bun build src/middleware.ts --outdir dist --target bun && bun build src/helpers.ts --outdir dist --target bun && bun build src/scheduler.ts --outdir dist --target bun && bun build bin/create.ts --outdir dist --target bun && bun build src/jsx.ts --outdir dist --target bun --format esm",
|
|
84
90
|
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
85
91
|
"build": "bun run build:js && bun run build:types",
|
|
86
92
|
"test": "bun test",
|