weifuwu 0.2.2 → 0.2.4

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.
Files changed (45) hide show
  1. package/README.md +90 -5
  2. package/dist/compress.d.ts +6 -0
  3. package/dist/cookie.d.ts +12 -0
  4. package/dist/index.d.ts +21 -0
  5. package/dist/index.js +1486 -0
  6. package/dist/middleware.d.ts +21 -0
  7. package/dist/rate-limit.d.ts +8 -0
  8. package/dist/router.d.ts +55 -0
  9. package/dist/serve.d.ts +19 -0
  10. package/dist/static.d.ts +7 -0
  11. package/dist/tsx.d.ts +17 -0
  12. package/dist/types.d.ts +9 -0
  13. package/dist/upload.d.ts +14 -0
  14. package/dist/validate.d.ts +9 -0
  15. package/package.json +14 -2
  16. package/AGENTS.md +0 -105
  17. package/compress.ts +0 -69
  18. package/cookie.ts +0 -58
  19. package/index.ts +0 -21
  20. package/middleware.ts +0 -178
  21. package/rate-limit.ts +0 -68
  22. package/router.ts +0 -701
  23. package/serve.ts +0 -126
  24. package/static.ts +0 -113
  25. package/test/compress.test.ts +0 -106
  26. package/test/cookie.test.ts +0 -79
  27. package/test/fixtures/pages/about/page.tsx +0 -3
  28. package/test/fixtures/pages/blog/[slug]/load.ts +0 -3
  29. package/test/fixtures/pages/blog/[slug]/page.tsx +0 -3
  30. package/test/fixtures/pages/blog/[slug]/route.ts +0 -7
  31. package/test/fixtures/pages/blog/layout.tsx +0 -3
  32. package/test/fixtures/pages/layout.tsx +0 -12
  33. package/test/fixtures/pages/page.tsx +0 -3
  34. package/test/middleware.test.ts +0 -407
  35. package/test/rate-limit.test.ts +0 -94
  36. package/test/static.test.ts +0 -93
  37. package/test/tsx.test.ts +0 -285
  38. package/test/unode.test.ts +0 -401
  39. package/test/upload.test.ts +0 -130
  40. package/test/validate.test.ts +0 -133
  41. package/tsconfig.json +0 -13
  42. package/tsx.ts +0 -374
  43. package/types.ts +0 -23
  44. package/upload.ts +0 -101
  45. package/validate.ts +0 -88
package/dist/index.js ADDED
@@ -0,0 +1,1486 @@
1
+ // serve.ts
2
+ import http from "node:http";
3
+ async function readBody(req) {
4
+ const chunks = [];
5
+ for await (const chunk of req) {
6
+ chunks.push(chunk);
7
+ }
8
+ return Buffer.concat(chunks);
9
+ }
10
+ function createRequest(req, body) {
11
+ const url = new URL(req.url ?? "/", "http://localhost");
12
+ const query = Object.fromEntries(url.searchParams);
13
+ const headers = {};
14
+ for (const [key, value] of Object.entries(req.headers)) {
15
+ if (value !== void 0) {
16
+ headers[key] = Array.isArray(value) ? value.join(", ") : value;
17
+ }
18
+ }
19
+ const request = new Request(url.href, {
20
+ method: req.method?.toUpperCase() ?? "GET",
21
+ headers,
22
+ body: req.method !== "GET" && req.method !== "HEAD" && body.length > 0 ? body : null
23
+ });
24
+ return [request, query];
25
+ }
26
+ async function sendResponse(res, response) {
27
+ const headers = {};
28
+ response.headers.forEach((value, key) => {
29
+ headers[key] = value;
30
+ });
31
+ res.writeHead(response.status, response.statusText, headers);
32
+ if (response.body) {
33
+ const reader = response.body.getReader();
34
+ try {
35
+ while (true) {
36
+ const { done, value } = await reader.read();
37
+ if (done) break;
38
+ res.write(value);
39
+ }
40
+ } finally {
41
+ reader.releaseLock();
42
+ }
43
+ }
44
+ res.end();
45
+ }
46
+ function serve(handler, options) {
47
+ const port = options?.port ?? 0;
48
+ const hostname = options?.hostname ?? "0.0.0.0";
49
+ const server = http.createServer(async (req, res) => {
50
+ try {
51
+ const body = await readBody(req);
52
+ const [request, query] = createRequest(req, body);
53
+ const response = await handler(request, { params: {}, query });
54
+ await sendResponse(res, response);
55
+ } catch {
56
+ res.writeHead(500, { "Content-Type": "text/plain" });
57
+ res.end("Internal Server Error");
58
+ }
59
+ });
60
+ if (options?.websocket) {
61
+ server.on("upgrade", options.websocket);
62
+ }
63
+ let resolveReady;
64
+ const ready = new Promise((r) => {
65
+ resolveReady = r;
66
+ });
67
+ if (options?.signal) {
68
+ if (options.signal.aborted) {
69
+ server.close();
70
+ resolveReady();
71
+ return {
72
+ stop: () => {
73
+ },
74
+ ready,
75
+ get port() {
76
+ return 0;
77
+ },
78
+ get hostname() {
79
+ return hostname;
80
+ }
81
+ };
82
+ }
83
+ options.signal.addEventListener("abort", () => {
84
+ server.close();
85
+ }, { once: true });
86
+ }
87
+ server.listen(port, hostname, () => {
88
+ resolveReady();
89
+ });
90
+ return {
91
+ stop: () => {
92
+ server.close();
93
+ },
94
+ ready,
95
+ get port() {
96
+ const addr = server.address();
97
+ if (!addr || typeof addr === "string") return 0;
98
+ return addr.port;
99
+ },
100
+ get hostname() {
101
+ const addr = server.address();
102
+ if (!addr) return hostname;
103
+ return typeof addr === "string" ? addr : addr.address;
104
+ }
105
+ };
106
+ }
107
+
108
+ // router.ts
109
+ import { WebSocketServer } from "ws";
110
+ import { buildSchema, graphql } from "graphql";
111
+ import { makeExecutableSchema } from "@graphql-tools/schema";
112
+ import { streamText } from "ai";
113
+ var createTrieNode = () => ({
114
+ children: /* @__PURE__ */ new Map(),
115
+ handlers: /* @__PURE__ */ new Map(),
116
+ middlewares: /* @__PURE__ */ new Map(),
117
+ pathMws: []
118
+ });
119
+ var createWsNode = () => ({
120
+ children: /* @__PURE__ */ new Map(),
121
+ middlewares: []
122
+ });
123
+ var getTrieNode = (node, segment) => {
124
+ if (segment.startsWith(":")) {
125
+ if (!node.children.has(":")) {
126
+ const child2 = createTrieNode();
127
+ child2.param = segment.slice(1);
128
+ node.children.set(":", child2);
129
+ }
130
+ const child = node.children.get(":");
131
+ if (child.param !== segment.slice(1)) {
132
+ throw new Error(
133
+ `Param name conflict: ":${child.param}" already registered at this path position, cannot register ":"${segment.slice(1)}"`
134
+ );
135
+ }
136
+ return child;
137
+ }
138
+ if (!node.children.has(segment)) {
139
+ node.children.set(segment, createTrieNode());
140
+ }
141
+ return node.children.get(segment);
142
+ };
143
+ var matchTrieNode = (node, segment, params) => {
144
+ if (node.children.has(segment)) return node.children.get(segment);
145
+ if (node.children.has(":")) {
146
+ const child = node.children.get(":");
147
+ if (child.param) params[child.param] = segment;
148
+ return child;
149
+ }
150
+ return null;
151
+ };
152
+ var getWsNode = (node, segment) => {
153
+ if (segment.startsWith(":")) {
154
+ if (!node.children.has(":")) {
155
+ const child2 = createWsNode();
156
+ child2.param = segment.slice(1);
157
+ node.children.set(":", child2);
158
+ }
159
+ const child = node.children.get(":");
160
+ if (child.param !== segment.slice(1)) {
161
+ throw new Error(
162
+ `Param name conflict: ":${child.param}" already registered at this path position`
163
+ );
164
+ }
165
+ return child;
166
+ }
167
+ if (!node.children.has(segment)) {
168
+ node.children.set(segment, createWsNode());
169
+ }
170
+ return node.children.get(segment);
171
+ };
172
+ var matchWsNode = (node, segment, params) => {
173
+ if (node.children.has(segment)) return node.children.get(segment);
174
+ if (node.children.has(":")) {
175
+ const child = node.children.get(":");
176
+ if (child.param) params[child.param] = segment;
177
+ return child;
178
+ }
179
+ return null;
180
+ };
181
+ var Router = class _Router {
182
+ root = createTrieNode();
183
+ wsRoot = createWsNode();
184
+ globalMws = [];
185
+ errorHandler;
186
+ use(arg1, arg2) {
187
+ if (typeof arg1 === "string") {
188
+ if (arg2 instanceof _Router) {
189
+ let node = this.root;
190
+ for (const segment of this.splitPath(arg1)) {
191
+ node = getTrieNode(node, segment);
192
+ }
193
+ node.subRouter = arg2;
194
+ } else if (typeof arg2 === "function") {
195
+ let node = this.root;
196
+ for (const segment of this.splitPath(arg1)) {
197
+ node = getTrieNode(node, segment);
198
+ }
199
+ node.pathMws.push(arg2);
200
+ }
201
+ } else if (typeof arg1 === "function") {
202
+ this.globalMws.push(arg1);
203
+ }
204
+ return this;
205
+ }
206
+ get(path, ...args) {
207
+ return this.route("GET", path, ...args);
208
+ }
209
+ post(path, ...args) {
210
+ return this.route("POST", path, ...args);
211
+ }
212
+ put(path, ...args) {
213
+ return this.route("PUT", path, ...args);
214
+ }
215
+ delete(path, ...args) {
216
+ return this.route("DELETE", path, ...args);
217
+ }
218
+ patch(path, ...args) {
219
+ return this.route("PATCH", path, ...args);
220
+ }
221
+ head(path, ...args) {
222
+ return this.route("HEAD", path, ...args);
223
+ }
224
+ options(path, ...args) {
225
+ return this.route("OPTIONS", path, ...args);
226
+ }
227
+ all(path, ...args) {
228
+ return this.route("*", path, ...args);
229
+ }
230
+ onError(handler) {
231
+ this.errorHandler = handler;
232
+ return this;
233
+ }
234
+ route(method, path, ...args) {
235
+ const handler = args.pop();
236
+ const middlewares = args;
237
+ const segments = this.splitPath(path);
238
+ let node = this.root;
239
+ for (const segment of segments) {
240
+ if (segment === "*") {
241
+ node.wildcard = true;
242
+ node.handlers.set(method, handler);
243
+ if (middlewares.length > 0) node.middlewares.set(method, middlewares);
244
+ return this;
245
+ }
246
+ node = getTrieNode(node, segment);
247
+ }
248
+ node.handlers.set(method, handler);
249
+ if (middlewares.length > 0) node.middlewares.set(method, middlewares);
250
+ return this;
251
+ }
252
+ ws(path, ...args) {
253
+ const handler = args.pop();
254
+ const middlewares = args;
255
+ const segments = this.splitPath(path);
256
+ let node = this.wsRoot;
257
+ for (const segment of segments) {
258
+ node = getWsNode(node, segment);
259
+ }
260
+ node.handler = handler;
261
+ if (middlewares.length > 0) node.middlewares = middlewares;
262
+ return this;
263
+ }
264
+ graphql(path, ...args) {
265
+ const options = args.pop();
266
+ const middlewares = args;
267
+ const schema = typeof options.schema === "string" ? options.resolvers ? makeExecutableSchema({
268
+ typeDefs: options.schema,
269
+ resolvers: options.resolvers
270
+ }) : buildSchema(options.schema) : options.schema;
271
+ const handler = (req, ctx) => {
272
+ const url = new URL(req.url);
273
+ if (options.graphiql && req.method === "GET" && !url.searchParams.has("query")) {
274
+ return new Response(getGraphiQLHtml(url.pathname), {
275
+ status: 200,
276
+ headers: { "Content-Type": "text/html" }
277
+ });
278
+ }
279
+ if (req.method !== "GET" && req.method !== "POST") {
280
+ return new Response("Not Found", { status: 404 });
281
+ }
282
+ const paramsPromise = req.method === "GET" ? Promise.resolve(parseGraphQLParamsFromGet(url)) : parseGraphQLParamsFromPost(req);
283
+ return paramsPromise.then((params) => {
284
+ if (!params) {
285
+ return Response.json(
286
+ { errors: [{ message: "Missing query" }] },
287
+ { status: 400 }
288
+ );
289
+ }
290
+ return executeGraphQLQuery(schema, params, options, req, ctx);
291
+ });
292
+ };
293
+ return this.all(path, ...middlewares, handler);
294
+ }
295
+ ai(path, ...args) {
296
+ const handler = args.pop();
297
+ const middlewares = args;
298
+ const routeHandler = async (req, ctx) => {
299
+ const options = await handler(req, ctx);
300
+ const result = streamText(options);
301
+ return result.toTextStreamResponse();
302
+ };
303
+ return this.post(path, ...middlewares, routeHandler);
304
+ }
305
+ handler() {
306
+ return (req, ctx) => {
307
+ const url = new URL(req.url);
308
+ return this.handle(req, ctx, this.splitPath(url.pathname), Object.fromEntries(url.searchParams));
309
+ };
310
+ }
311
+ websocketHandler() {
312
+ const wss = new WebSocketServer({ noServer: true });
313
+ const wsRoot = this.wsRoot;
314
+ const router = this;
315
+ return (req, socket, head) => {
316
+ const url = new URL(req.url ?? "/", "http://localhost");
317
+ const segments = url.pathname.split("/").filter(Boolean);
318
+ const query = Object.fromEntries(url.searchParams);
319
+ const match = router.matchWsTrie(wsRoot, segments);
320
+ if (!match) {
321
+ socket.destroy();
322
+ return;
323
+ }
324
+ const webReq = new Request(url.href, {
325
+ method: req.method ?? "GET",
326
+ headers: Object.fromEntries(
327
+ Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : v ?? ""])
328
+ )
329
+ });
330
+ const ctx = { params: match.params, query };
331
+ if (match.middlewares.length === 0) {
332
+ upgradeSocket(wss, req, socket, head, match.handler, ctx);
333
+ return;
334
+ }
335
+ let index = 0;
336
+ const dispatch = async (innerReq, ctx2) => {
337
+ if (index < match.middlewares.length) {
338
+ const mw = match.middlewares[index++];
339
+ return mw(innerReq, ctx2, dispatch);
340
+ }
341
+ return await new Promise((resolve3) => {
342
+ upgradeSocket(wss, req, socket, head, match.handler, ctx2);
343
+ resolve3(new Response(null, { status: 101 }));
344
+ });
345
+ };
346
+ Promise.resolve(dispatch(webReq, ctx)).then((result) => {
347
+ if (result.status !== 101) {
348
+ sendHttpResponseOnSocket(socket, result);
349
+ }
350
+ }).catch(() => {
351
+ socket.destroy();
352
+ });
353
+ };
354
+ }
355
+ splitPath(path) {
356
+ return path.split("/").filter(Boolean);
357
+ }
358
+ matchTrie(method, segments) {
359
+ let node = this.root;
360
+ const params = {};
361
+ const pathMws = [...this.root.pathMws];
362
+ let wildcardHandler = null;
363
+ let wildcardMws = [];
364
+ let wildcardIdx = -1;
365
+ for (let i = 0; i < segments.length; i++) {
366
+ pathMws.push(...node.pathMws);
367
+ if (node.wildcard) {
368
+ const h = node.handlers.get("*") || node.handlers.get(method);
369
+ if (h) {
370
+ wildcardHandler = h;
371
+ wildcardMws = node.middlewares.get(method) || node.middlewares.get("*") || [];
372
+ wildcardIdx = i;
373
+ }
374
+ }
375
+ const segment = segments[i];
376
+ if (!segment) break;
377
+ const next = matchTrieNode(node, segment, params);
378
+ if (!next) {
379
+ if (node.subRouter) {
380
+ return {
381
+ pathMws,
382
+ params,
383
+ middlewares: [],
384
+ subRouter: { router: node.subRouter, remainingIdx: i }
385
+ };
386
+ }
387
+ if (wildcardHandler) {
388
+ params["*"] = segments.slice(wildcardIdx).join("/");
389
+ return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
390
+ }
391
+ return null;
392
+ }
393
+ node = next;
394
+ }
395
+ if (node.subRouter) {
396
+ return {
397
+ pathMws,
398
+ params,
399
+ middlewares: [],
400
+ subRouter: { router: node.subRouter, remainingIdx: segments.length }
401
+ };
402
+ }
403
+ pathMws.push(...node.pathMws);
404
+ const handler = node.handlers.get(method) || node.handlers.get("*");
405
+ if (handler) {
406
+ if (node.wildcard) params["*"] = segments.slice(segments.length).join("/");
407
+ return {
408
+ handler,
409
+ middlewares: node.middlewares.get(method) || node.middlewares.get("*") || [],
410
+ pathMws,
411
+ params
412
+ };
413
+ }
414
+ if (wildcardHandler) {
415
+ params["*"] = segments.slice(wildcardIdx).join("/");
416
+ return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
417
+ }
418
+ return null;
419
+ }
420
+ matchWsTrie(root, segments) {
421
+ let node = root;
422
+ const params = {};
423
+ for (const segment of segments) {
424
+ const next = matchWsNode(node, segment, params);
425
+ if (!next) return null;
426
+ node = next;
427
+ }
428
+ return node.handler ? { handler: node.handler, middlewares: node.middlewares, params } : null;
429
+ }
430
+ async handle(req, ctx, segments, query) {
431
+ const match = this.matchTrie(req.method, segments);
432
+ if (match?.subRouter) {
433
+ const { router: sub, remainingIdx } = match.subRouter;
434
+ const remainingSegments = segments.slice(remainingIdx);
435
+ const delegate = (req2, ctx2) => sub.handle(req2, ctx2, remainingSegments, query);
436
+ const allMws = this.globalMws.length + match.pathMws.length === 0 ? [] : [...this.globalMws, ...match.pathMws];
437
+ try {
438
+ return await this.runChain(allMws, delegate, req, { ...ctx, params: { ...ctx.params, ...match.params } });
439
+ } catch (e) {
440
+ return this.errorHandler ? this.errorHandler(e, req, ctx) : new Response("Internal Server Error", { status: 500 });
441
+ }
442
+ }
443
+ if (match?.handler) {
444
+ const { handler, middlewares: routeMws, pathMws, params } = match;
445
+ const allMws = this.globalMws.length + pathMws.length + routeMws.length === 0 ? [] : [...this.globalMws, ...pathMws, ...routeMws];
446
+ const ctxWithMatch = { ...ctx, params: { ...ctx.params, ...params } };
447
+ try {
448
+ return await this.runChain(allMws, handler, req, ctxWithMatch);
449
+ } catch (e) {
450
+ return this.errorHandler ? this.errorHandler(e, req, ctxWithMatch) : new Response("Internal Server Error", { status: 500 });
451
+ }
452
+ }
453
+ if (this.globalMws.length > 0) {
454
+ try {
455
+ const delegate = () => new Response("Not Found", { status: 404 });
456
+ return await this.runChain(this.globalMws, delegate, req, ctx);
457
+ } catch (e) {
458
+ return this.errorHandler ? this.errorHandler(e, req, ctx) : new Response("Internal Server Error", { status: 500 });
459
+ }
460
+ }
461
+ return new Response("Not Found", { status: 404 });
462
+ }
463
+ async runChain(middlewares, finalHandler, req, ctx) {
464
+ let index = 0;
465
+ const dispatch = async (req2, ctx2) => {
466
+ if (index < middlewares.length) {
467
+ const mw = middlewares[index++];
468
+ return mw ? await mw(req2, ctx2, dispatch) : new Response("Middleware error", { status: 500 });
469
+ }
470
+ return await finalHandler(req2, ctx2);
471
+ };
472
+ return dispatch(req, ctx);
473
+ }
474
+ };
475
+ function upgradeSocket(wss, req, socket, head, handler, ctx) {
476
+ wss.handleUpgrade(req, socket, head, (ws) => {
477
+ if (handler.open) {
478
+ handler.open(ws, ctx);
479
+ }
480
+ ws.on("message", (data) => {
481
+ handler.message?.(ws, ctx, data);
482
+ });
483
+ ws.on("close", () => {
484
+ handler.close?.(ws, ctx);
485
+ });
486
+ ws.on("error", (err) => {
487
+ handler.error?.(ws, ctx, err);
488
+ });
489
+ });
490
+ }
491
+ function sendHttpResponseOnSocket(socket, response) {
492
+ const statusLine = `HTTP/1.1 ${response.status} ${response.statusText}`;
493
+ const headerLines = [statusLine];
494
+ response.headers.forEach((value, key) => {
495
+ headerLines.push(`${key}: ${value}`);
496
+ });
497
+ headerLines.push("Connection: close");
498
+ headerLines.push("");
499
+ const headerStr = headerLines.join("\r\n");
500
+ response.arrayBuffer().then((buf) => {
501
+ const body = Buffer.from(buf);
502
+ socket.write(headerStr + "\r\n" + body.toString());
503
+ socket.end();
504
+ }).catch(() => {
505
+ socket.write(headerStr + "\r\n");
506
+ socket.end();
507
+ });
508
+ }
509
+ function parseGraphQLParamsFromGet(url) {
510
+ const query = url.searchParams.get("query");
511
+ if (!query) return null;
512
+ const variablesStr = url.searchParams.get("variables");
513
+ let variables = {};
514
+ if (variablesStr) {
515
+ try {
516
+ variables = JSON.parse(variablesStr);
517
+ } catch {
518
+ return null;
519
+ }
520
+ }
521
+ return {
522
+ query,
523
+ variables,
524
+ operationName: url.searchParams.get("operationName") || void 0
525
+ };
526
+ }
527
+ async function parseGraphQLParamsFromPost(req) {
528
+ try {
529
+ const body = await req.json();
530
+ if (!body.query) return null;
531
+ return {
532
+ query: body.query,
533
+ variables: body.variables || {},
534
+ operationName: body.operationName
535
+ };
536
+ } catch {
537
+ return null;
538
+ }
539
+ }
540
+ async function executeGraphQLQuery(schema, params, options, req, ctx) {
541
+ const contextValue = options.context ? await options.context(req, ctx) : ctx;
542
+ const result = await graphql({
543
+ schema,
544
+ source: params.query,
545
+ rootValue: options.rootValue,
546
+ contextValue,
547
+ variableValues: params.variables,
548
+ operationName: params.operationName
549
+ });
550
+ return Response.json(result, { status: result.errors ? 400 : 200 });
551
+ }
552
+ function getGraphiQLHtml(endpoint) {
553
+ return `<!doctype html>
554
+ <html lang="en">
555
+ <head>
556
+ <meta charset="UTF-8" />
557
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
558
+ <title>GraphiQL</title>
559
+ <style>
560
+ body { margin: 0; }
561
+ #graphiql { height: 100dvh; }
562
+ </style>
563
+ <link rel="stylesheet" href="https://esm.sh/graphiql@5.2.2/dist/style.css" />
564
+ <script type="importmap">
565
+ {
566
+ "imports": {
567
+ "react": "https://esm.sh/react@19.2.5",
568
+ "react/": "https://esm.sh/react@19.2.5/",
569
+ "react-dom": "https://esm.sh/react-dom@19.2.5",
570
+ "react-dom/": "https://esm.sh/react-dom@19.2.5/",
571
+ "graphiql": "https://esm.sh/graphiql@5.2.2?standalone&external=react,react-dom,@graphiql/react,graphql",
572
+ "graphiql/": "https://esm.sh/graphiql@5.2.2/",
573
+ "@graphiql/react": "https://esm.sh/@graphiql/react@0.37.3?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid",
574
+ "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.11.3?standalone&external=graphql",
575
+ "graphql": "https://esm.sh/graphql@16.13.2",
576
+ "@emotion/is-prop-valid": "data:text/javascript,"
577
+ }
578
+ }
579
+ </script>
580
+ <script type="module">
581
+ import React from 'react';
582
+ import ReactDOM from 'react-dom/client';
583
+ import { GraphiQL } from 'graphiql';
584
+ import { createGraphiQLFetcher } from '@graphiql/toolkit';
585
+ import 'graphiql/setup-workers/esm.sh';
586
+
587
+ const fetcher = createGraphiQLFetcher({ url: "${endpoint}" });
588
+
589
+ function App() {
590
+ return React.createElement(GraphiQL, { fetcher });
591
+ }
592
+
593
+ const container = document.getElementById('graphiql');
594
+ const root = ReactDOM.createRoot(container);
595
+ root.render(React.createElement(App));
596
+ </script>
597
+ </head>
598
+ <body>
599
+ <div id="graphiql">Loading\u2026</div>
600
+ </body>
601
+ </html>`;
602
+ }
603
+
604
+ // tsx.ts
605
+ import { createElement, createContext, useContext } from "react";
606
+ import { renderToReadableStream } from "react-dom/server";
607
+ import * as esbuild from "esbuild";
608
+ import { readdirSync, statSync, existsSync, mkdirSync } from "node:fs";
609
+ import { join, relative, resolve, sep, dirname } from "node:path";
610
+ import { pathToFileURL } from "node:url";
611
+ import { createHash } from "node:crypto";
612
+ var TsxContext = createContext({ params: {}, query: {} });
613
+ function useTsx() {
614
+ return useContext(TsxContext);
615
+ }
616
+ function id(s) {
617
+ return createHash("md5").update(s).digest("hex").slice(0, 8);
618
+ }
619
+ function concatUint8(chunks) {
620
+ const len = chunks.reduce((a, c) => a + c.length, 0);
621
+ const out = new Uint8Array(len);
622
+ let off = 0;
623
+ for (const c of chunks) {
624
+ out.set(c, off);
625
+ off += c.length;
626
+ }
627
+ return out;
628
+ }
629
+ async function readStream(stream) {
630
+ const chunks = [];
631
+ const reader = stream.getReader();
632
+ while (true) {
633
+ const { done, value } = await reader.read();
634
+ if (done) break;
635
+ chunks.push(value);
636
+ }
637
+ return new TextDecoder().decode(concatUint8(chunks));
638
+ }
639
+ function scanPages(dir) {
640
+ const pages = [];
641
+ function walk(current) {
642
+ let entries;
643
+ try {
644
+ entries = readdirSync(current);
645
+ } catch {
646
+ return;
647
+ }
648
+ const dirs = [];
649
+ for (const name of entries) {
650
+ const full = join(current, name);
651
+ const st = statSync(full);
652
+ if (st.isDirectory()) {
653
+ if (!name.startsWith(".")) dirs.push(full);
654
+ }
655
+ }
656
+ const pagePath = join(current, "page.tsx");
657
+ const tsPagePath = join(current, "page.ts");
658
+ let entryPath = "";
659
+ if (existsSync(pagePath)) {
660
+ entryPath = pagePath;
661
+ } else if (existsSync(tsPagePath)) {
662
+ entryPath = tsPagePath;
663
+ }
664
+ if (entryPath) {
665
+ let relPath = relative(dir, entryPath).replace(sep, "/");
666
+ relPath = relPath.replace(/\/page\.tsx?$/, "");
667
+ relPath = relPath.replace(/^page\.tsx?$/, "");
668
+ const route = filePathToRoute(relPath);
669
+ const layouts = resolveLayouts(current, dir);
670
+ const loadPath = existsSync(join(current, "load.ts")) ? join(current, "load.ts") : void 0;
671
+ const rPath = existsSync(join(current, "route.ts")) ? join(current, "route.ts") : void 0;
672
+ pages.push({
673
+ route,
674
+ entryPath,
675
+ loadPath,
676
+ layouts,
677
+ routePath: rPath
678
+ });
679
+ } else {
680
+ const rPath = join(current, "route.ts");
681
+ if (existsSync(rPath)) {
682
+ let relPath = relative(dir, rPath).replace(sep, "/");
683
+ relPath = relPath.replace(/\/route\.tsx?$/, "");
684
+ const route = filePathToRoute(relPath);
685
+ pages.push({
686
+ route,
687
+ entryPath: "",
688
+ layouts: [],
689
+ routePath: rPath,
690
+ routeOnly: true
691
+ });
692
+ }
693
+ }
694
+ for (const d of dirs) walk(d);
695
+ }
696
+ walk(dir);
697
+ return pages;
698
+ }
699
+ function filePathToRoute(relPath) {
700
+ let route = relPath.replace(/\\/g, "/");
701
+ route = route.replace(/\[\.\.\.(\w+)\]/g, "*");
702
+ route = route.replace(/\[(\w+)\]/g, ":$1");
703
+ return route.startsWith("/") ? route : "/" + route;
704
+ }
705
+ function resolveLayouts(dir, pagesDir) {
706
+ const layouts = [];
707
+ let current = dir;
708
+ while (current.startsWith(pagesDir)) {
709
+ const p = join(current, "layout.tsx");
710
+ if (existsSync(p)) {
711
+ layouts.push(p);
712
+ }
713
+ const parent = dirname(current);
714
+ if (parent === current) break;
715
+ current = parent;
716
+ }
717
+ return layouts.reverse();
718
+ }
719
+ async function compileAll(files, outDir, platform) {
720
+ const entryPoints = {};
721
+ for (const f of files) {
722
+ entryPoints[id(f)] = f;
723
+ }
724
+ const isBrowser = platform === "browser";
725
+ await esbuild.build({
726
+ entryPoints,
727
+ outdir: outDir,
728
+ format: "esm",
729
+ platform: "node",
730
+ jsx: "automatic",
731
+ jsxImportSource: "react",
732
+ bundle: true,
733
+ external: isBrowser ? void 0 : [
734
+ "react",
735
+ "react-dom",
736
+ "esbuild",
737
+ "graphql",
738
+ "ws",
739
+ "zod",
740
+ "@graphql-tools/schema",
741
+ "ai"
742
+ ],
743
+ write: true,
744
+ allowOverwrite: true
745
+ });
746
+ }
747
+ function compiledUrl(filePath, outDir) {
748
+ const hash = id(join(outDir, id(filePath)));
749
+ const p = join(outDir, id(filePath) + ".js");
750
+ return pathToFileURL(p).href;
751
+ }
752
+ var clientBundleCache = /* @__PURE__ */ new Map();
753
+ var clientRouteLog = /* @__PURE__ */ new WeakMap();
754
+ async function getOrBuildClientBundle(entryPath, layoutPaths, pagesDir, router) {
755
+ const key = id(entryPath);
756
+ const url = `/__wfw/client/${key}.js`;
757
+ if (!clientRouteLog.get(router)?.has(url)) {
758
+ let buf = clientBundleCache.get(key);
759
+ if (!buf) {
760
+ try {
761
+ const nested = layoutPaths.slice(1);
762
+ const layoutsImport = nested.map(
763
+ (p, i) => `import L${i} from${JSON.stringify(p)};`
764
+ ).join("");
765
+ const layoutsWrap = nested.map((_, i) => {
766
+ const idx = nested.length - 1 - i;
767
+ return `el=createElement(L${idx},null,el);`;
768
+ }).join("");
769
+ const code = [
770
+ `import{hydrateRoot}from'react-dom/client';`,
771
+ `import{createElement}from'react';`,
772
+ `import P from${JSON.stringify(entryPath)};`,
773
+ layoutsImport,
774
+ `const p=window.__WEIFUWU_PROPS;`,
775
+ `let el=createElement(P,p);`,
776
+ layoutsWrap,
777
+ `hydrateRoot(document.getElementById('__weifuwu_root'),el);`
778
+ ].join("");
779
+ const result = await esbuild.build({
780
+ stdin: { contents: code, loader: "tsx", resolveDir: pagesDir },
781
+ bundle: true,
782
+ format: "esm",
783
+ jsx: "automatic",
784
+ jsxImportSource: "react",
785
+ write: false,
786
+ minify: true
787
+ });
788
+ buf = result.outputFiles[0].contents;
789
+ clientBundleCache.set(key, buf);
790
+ } catch (err) {
791
+ console.error("hydration bundle failed:", err);
792
+ return null;
793
+ }
794
+ }
795
+ router.get(url, () => new Response(buf, {
796
+ headers: { "content-type": "application/javascript; charset=utf-8" }
797
+ }));
798
+ const set = clientRouteLog.get(router) ?? /* @__PURE__ */ new Set();
799
+ set.add(url);
800
+ clientRouteLog.set(router, set);
801
+ }
802
+ return { url };
803
+ }
804
+ function makeSsrHandler(Component, loadFn, layouts, entryPath, layoutPaths, pagesDir, router) {
805
+ return async (req, ctx) => {
806
+ const loadProps = loadFn ? await loadFn({ params: ctx.params, query: ctx.query }) : {};
807
+ const allProps = { ...loadProps, params: ctx.params, query: ctx.query };
808
+ let element = createElement(Component, allProps);
809
+ for (let i = layouts.length - 1; i >= 0; i--) {
810
+ const isRoot = i === 0;
811
+ element = createElement(
812
+ layouts[i],
813
+ isRoot ? { children: element, req, ctx } : { children: element }
814
+ );
815
+ }
816
+ element = createElement(TsxContext.Provider, {
817
+ value: { params: ctx.params, query: ctx.query, user: ctx.user, parsed: ctx.parsed }
818
+ }, element);
819
+ const stream = await renderToReadableStream(element);
820
+ const body = await readStream(stream);
821
+ const scripts = [];
822
+ scripts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(allProps)}</script>`);
823
+ const bundle = await getOrBuildClientBundle(entryPath, layoutPaths, pagesDir, router);
824
+ if (bundle) {
825
+ scripts.push(`<script type="module" src="${bundle.url}"></script>`);
826
+ }
827
+ const html = `<!DOCTYPE html>
828
+ ${body}
829
+ ${scripts.join("\n")}`;
830
+ return new Response(html, {
831
+ headers: { "content-type": "text/html; charset=utf-8" }
832
+ });
833
+ };
834
+ }
835
+ async function tsx(options) {
836
+ const pagesDir = resolve(options.dir);
837
+ const outDir = join(pagesDir, "..", ".weifuwu", "ssr");
838
+ const pages = scanPages(pagesDir);
839
+ if (pages.length === 0) return new Router();
840
+ const allFiles = /* @__PURE__ */ new Set();
841
+ for (const p of pages) {
842
+ if (p.entryPath) allFiles.add(p.entryPath);
843
+ if (p.loadPath) allFiles.add(p.loadPath);
844
+ for (const lp of p.layouts) allFiles.add(lp);
845
+ if (p.routePath) allFiles.add(p.routePath);
846
+ }
847
+ const nfPath = join(pagesDir, "not-found.tsx");
848
+ const hasNotFound = existsSync(nfPath);
849
+ if (hasNotFound) {
850
+ allFiles.add(nfPath);
851
+ const rootLayouts = resolveLayouts(pagesDir, pagesDir);
852
+ for (const lp of rootLayouts) allFiles.add(lp);
853
+ }
854
+ mkdirSync(outDir, { recursive: true });
855
+ await compileAll([...allFiles], outDir, "node");
856
+ const router = new Router();
857
+ for (const p of pages) {
858
+ if (p.routeOnly && p.routePath) {
859
+ const rUrl = compiledUrl(p.routePath, outDir);
860
+ const modR = await import(rUrl);
861
+ const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
862
+ for (const method of methods) {
863
+ if (modR[method]) {
864
+ router.route(method, p.route, modR[method]);
865
+ }
866
+ }
867
+ continue;
868
+ }
869
+ const url = compiledUrl(p.entryPath, outDir);
870
+ const mod = await import(url);
871
+ const Component = mod.default;
872
+ let loadFn;
873
+ if (p.loadPath) {
874
+ const loadUrl = compiledUrl(p.loadPath, outDir);
875
+ const modLoad = await import(loadUrl);
876
+ loadFn = modLoad.default;
877
+ }
878
+ const layoutComponents = [];
879
+ for (const lp of p.layouts) {
880
+ const lUrl = compiledUrl(lp, outDir);
881
+ const modL = await import(lUrl);
882
+ layoutComponents.push(modL.default);
883
+ }
884
+ const handler = makeSsrHandler(
885
+ Component,
886
+ loadFn,
887
+ layoutComponents,
888
+ p.entryPath,
889
+ p.layouts,
890
+ pagesDir,
891
+ router
892
+ );
893
+ router.get(p.route, handler);
894
+ if (p.routePath) {
895
+ const rUrl = compiledUrl(p.routePath, outDir);
896
+ const modR = await import(rUrl);
897
+ const methods = ["POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
898
+ for (const method of methods) {
899
+ if (modR[method]) {
900
+ router.route(method, p.route, modR[method]);
901
+ }
902
+ }
903
+ }
904
+ }
905
+ if (hasNotFound) {
906
+ const nfUrl = compiledUrl(nfPath, outDir);
907
+ const modNf = await import(nfUrl);
908
+ const NfComponent = modNf.default;
909
+ const nfLayouts = [];
910
+ const rootLayouts = resolveLayouts(pagesDir, pagesDir);
911
+ for (const lp of rootLayouts) {
912
+ const lUrl = compiledUrl(lp, outDir);
913
+ const modL = await import(lUrl);
914
+ nfLayouts.push(modL.default);
915
+ }
916
+ const handler = async (req, ctx) => {
917
+ let element = createElement(NfComponent, { params: ctx.params, query: ctx.query });
918
+ for (let i = nfLayouts.length - 1; i >= 0; i--) {
919
+ element = createElement(nfLayouts[i], { children: element });
920
+ }
921
+ element = createElement(TsxContext.Provider, {
922
+ value: { params: ctx.params, query: ctx.query, user: ctx.user, parsed: ctx.parsed }
923
+ }, element);
924
+ const stream = await renderToReadableStream(element);
925
+ const body = await readStream(stream);
926
+ const html = `<!DOCTYPE html>
927
+ ${body}`;
928
+ return new Response(html, {
929
+ status: 404,
930
+ headers: { "content-type": "text/html; charset=utf-8" }
931
+ });
932
+ };
933
+ router.all("/*", handler);
934
+ }
935
+ return router;
936
+ }
937
+
938
+ // middleware.ts
939
+ function logger(options) {
940
+ return async (req, ctx, next) => {
941
+ const start = Date.now();
942
+ const url = new URL(req.url);
943
+ const res = await next(req, ctx);
944
+ const ms = Date.now() - start;
945
+ if (options?.format === "combined") {
946
+ console.log(`${req.method} ${url.pathname}${url.search} ${res.status} ${ms}ms`);
947
+ } else {
948
+ console.log(`${req.method} ${url.pathname} ${res.status} ${ms}ms`);
949
+ }
950
+ return res;
951
+ };
952
+ }
953
+ function cors(options) {
954
+ const opts = {
955
+ origin: "*",
956
+ methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
957
+ allowedHeaders: ["Content-Type", "Authorization"],
958
+ ...options
959
+ };
960
+ function resolveOrigin(requestOrigin) {
961
+ if (typeof opts.origin === "string") return opts.origin === "*" ? "*" : opts.origin;
962
+ if (Array.isArray(opts.origin)) {
963
+ return opts.origin.includes(requestOrigin) ? requestOrigin : "";
964
+ }
965
+ const result = opts.origin(requestOrigin);
966
+ if (typeof result === "boolean") return result ? requestOrigin : "";
967
+ if (typeof result === "string") return result;
968
+ return "";
969
+ }
970
+ function setCORSHeaders(res, acao) {
971
+ if (!acao) return res;
972
+ const headers = new Headers(res.headers);
973
+ headers.set("Access-Control-Allow-Origin", acao);
974
+ if (opts.credentials) headers.set("Access-Control-Allow-Credentials", "true");
975
+ if (opts.exposedHeaders?.length) headers.set("Access-Control-Expose-Headers", opts.exposedHeaders.join(", "));
976
+ headers.set("Vary", "Origin");
977
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
978
+ }
979
+ return (req, ctx, next) => {
980
+ const requestOrigin = req.headers.get("origin") ?? "";
981
+ const acao = resolveOrigin(requestOrigin);
982
+ if (req.method === "OPTIONS" && acao) {
983
+ const headers = new Headers();
984
+ headers.set("Access-Control-Allow-Origin", acao);
985
+ headers.set("Access-Control-Allow-Methods", opts.methods.join(", "));
986
+ headers.set("Access-Control-Allow-Headers", opts.allowedHeaders.join(", "));
987
+ if (opts.credentials) headers.set("Access-Control-Allow-Credentials", "true");
988
+ if (opts.maxAge != null) headers.set("Access-Control-Max-Age", String(opts.maxAge));
989
+ headers.set("Vary", "Origin");
990
+ return new Response(null, { status: 204, headers });
991
+ }
992
+ if (!acao) return next(req, ctx);
993
+ return Promise.resolve(next(req, ctx)).then((res) => setCORSHeaders(res, acao));
994
+ };
995
+ }
996
+ function auth(options) {
997
+ return async (req, ctx, next) => {
998
+ const headerName = options.header ?? "Authorization";
999
+ let from = "header";
1000
+ let header = req.headers.get(headerName);
1001
+ let token = "";
1002
+ if (header) {
1003
+ token = header;
1004
+ if (headerName.toLowerCase() === "authorization") {
1005
+ const parts = header.split(" ");
1006
+ if (parts[0]?.toLowerCase() === "bearer") {
1007
+ token = parts.slice(1).join(" ");
1008
+ }
1009
+ }
1010
+ } else {
1011
+ const url = new URL(req.url);
1012
+ const qsToken = url.searchParams.get("access_token");
1013
+ if (qsToken) {
1014
+ token = qsToken;
1015
+ from = "query";
1016
+ }
1017
+ }
1018
+ if (!token) {
1019
+ return new Response("Unauthorized", {
1020
+ status: 401,
1021
+ headers: headerName.toLowerCase() === "authorization" ? { "WWW-Authenticate": "Bearer" } : void 0
1022
+ });
1023
+ }
1024
+ if (options.proxy) {
1025
+ const proxyUrl = typeof options.proxy === "string" ? new URL(options.proxy) : options.proxy;
1026
+ const proxyHeaders = {};
1027
+ if (from === "header" && header) {
1028
+ proxyHeaders[headerName] = header;
1029
+ } else {
1030
+ proxyUrl.searchParams.set("access_token", token);
1031
+ }
1032
+ for (const name of ["x-forwarded-for", "x-real-ip", "user-agent", "content-type"]) {
1033
+ const v = req.headers.get(name);
1034
+ if (v) proxyHeaders[name] = v;
1035
+ }
1036
+ const proxyRes = await fetch(proxyUrl.href, { headers: proxyHeaders });
1037
+ if (proxyRes.status >= 400) {
1038
+ return new Response(await proxyRes.text() || "Forbidden", { status: proxyRes.status });
1039
+ }
1040
+ let userData = void 0;
1041
+ if (proxyRes.status === 200) {
1042
+ const ct = proxyRes.headers.get("content-type");
1043
+ if (ct?.includes("application/json")) {
1044
+ try {
1045
+ userData = await proxyRes.json();
1046
+ } catch {
1047
+ }
1048
+ }
1049
+ }
1050
+ ctx.user = userData;
1051
+ return next(req, ctx);
1052
+ }
1053
+ if (options.token) {
1054
+ if (token !== options.token) {
1055
+ return new Response("Forbidden", { status: 403 });
1056
+ }
1057
+ return next(req, ctx);
1058
+ }
1059
+ if (options.verify) {
1060
+ const result = await options.verify(token, req);
1061
+ if (!result) {
1062
+ return new Response("Forbidden", { status: 403 });
1063
+ }
1064
+ if (typeof result === "object" && result !== null) {
1065
+ ctx.user = result;
1066
+ }
1067
+ return next(req, ctx);
1068
+ }
1069
+ return next(req, ctx);
1070
+ };
1071
+ }
1072
+
1073
+ // static.ts
1074
+ import { createHash as createHash2 } from "node:crypto";
1075
+ import { open } from "node:fs/promises";
1076
+ import { extname, resolve as resolve2, normalize, sep as sep2 } from "node:path";
1077
+ function serveStatic(root, options) {
1078
+ const rootDir = resolve2(root);
1079
+ const opts = options ?? {};
1080
+ return async (req, ctx) => {
1081
+ const relativePath = ctx.params["*"] ?? new URL(req.url).pathname.slice(1);
1082
+ const decoded = decodeURIComponent(relativePath);
1083
+ if (decoded.includes("..") || decoded.includes("\0")) {
1084
+ return new Response("Forbidden", { status: 403 });
1085
+ }
1086
+ let filePath = normalize(resolve2(rootDir, decoded));
1087
+ if (!filePath.startsWith(rootDir + sep2) && filePath !== rootDir) {
1088
+ return new Response("Forbidden", { status: 403 });
1089
+ }
1090
+ let fileHandle;
1091
+ try {
1092
+ fileHandle = await open(filePath, "r");
1093
+ const stat = await fileHandle.stat();
1094
+ if (stat.isDirectory()) {
1095
+ await fileHandle.close();
1096
+ const indexFile = opts.index ?? "index.html";
1097
+ filePath = resolve2(filePath, indexFile);
1098
+ if (!filePath.startsWith(rootDir + sep2)) {
1099
+ return new Response("Forbidden", { status: 403 });
1100
+ }
1101
+ fileHandle = await open(filePath, "r");
1102
+ const dirStat = await fileHandle.stat();
1103
+ if (!dirStat.isFile()) {
1104
+ await fileHandle.close();
1105
+ return new Response("Not Found", { status: 404 });
1106
+ }
1107
+ }
1108
+ const mimeType = MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
1109
+ const etag = `"${createHash2("md5").update(`${stat.size}-${stat.mtimeMs}`).digest("hex")}"`;
1110
+ const ifNoneMatch = req.headers.get("if-none-match");
1111
+ if (ifNoneMatch === etag) {
1112
+ await fileHandle.close();
1113
+ return new Response(null, { status: 304 });
1114
+ }
1115
+ const ifModifiedSince = req.headers.get("if-modified-since");
1116
+ if (ifModifiedSince && stat.mtimeMs <= new Date(ifModifiedSince).getTime()) {
1117
+ await fileHandle.close();
1118
+ return new Response(null, { status: 304 });
1119
+ }
1120
+ const headers = {
1121
+ "Content-Type": mimeType,
1122
+ "Content-Length": String(stat.size),
1123
+ "ETag": etag,
1124
+ "Last-Modified": stat.mtime.toUTCString(),
1125
+ "Cache-Control": opts.immutable ? `public, max-age=${opts.maxAge ?? 31536e3}, immutable` : `public, max-age=${opts.maxAge ?? 0}`
1126
+ };
1127
+ const stream = fileHandle.readableWebStream();
1128
+ return new Response(stream, { headers });
1129
+ } catch (err) {
1130
+ if (fileHandle) await fileHandle.close().catch(() => {
1131
+ });
1132
+ if (err?.code === "ENOENT") {
1133
+ return new Response("Not Found", { status: 404 });
1134
+ }
1135
+ return new Response("Internal Server Error", { status: 500 });
1136
+ }
1137
+ };
1138
+ }
1139
+ var MIME_TYPES = {
1140
+ ".html": "text/html; charset=utf-8",
1141
+ ".htm": "text/html; charset=utf-8",
1142
+ ".css": "text/css; charset=utf-8",
1143
+ ".js": "application/javascript; charset=utf-8",
1144
+ ".mjs": "application/javascript; charset=utf-8",
1145
+ ".json": "application/json",
1146
+ ".png": "image/png",
1147
+ ".jpg": "image/jpeg",
1148
+ ".jpeg": "image/jpeg",
1149
+ ".gif": "image/gif",
1150
+ ".svg": "image/svg+xml",
1151
+ ".ico": "image/x-icon",
1152
+ ".webp": "image/webp",
1153
+ ".avif": "image/avif",
1154
+ ".woff": "font/woff",
1155
+ ".woff2": "font/woff2",
1156
+ ".ttf": "font/ttf",
1157
+ ".otf": "font/otf",
1158
+ ".eot": "application/vnd.ms-fontobject",
1159
+ ".txt": "text/plain; charset=utf-8",
1160
+ ".xml": "application/xml",
1161
+ ".pdf": "application/pdf",
1162
+ ".zip": "application/zip",
1163
+ ".wasm": "application/wasm",
1164
+ ".map": "application/json"
1165
+ };
1166
+
1167
+ // validate.ts
1168
+ function validate(schemas) {
1169
+ return async (req, ctx, next) => {
1170
+ const parsed = {};
1171
+ const issues = [];
1172
+ if (schemas.params) {
1173
+ const result = schemas.params.safeParse(ctx.params);
1174
+ if (result.success) {
1175
+ parsed.params = result.data;
1176
+ } else {
1177
+ issues.push(...result.error.issues.map((i) => ({
1178
+ path: ["params", ...i.path.map(String)],
1179
+ message: i.message
1180
+ })));
1181
+ }
1182
+ }
1183
+ if (schemas.query) {
1184
+ const result = schemas.query.safeParse(ctx.query);
1185
+ if (result.success) {
1186
+ parsed.query = result.data;
1187
+ } else {
1188
+ issues.push(...result.error.issues.map((i) => ({
1189
+ path: ["query", ...i.path.map(String)],
1190
+ message: i.message
1191
+ })));
1192
+ }
1193
+ }
1194
+ if (schemas.headers) {
1195
+ const rawHeaders = {};
1196
+ req.headers.forEach((v, k) => {
1197
+ rawHeaders[k] = v;
1198
+ });
1199
+ const result = schemas.headers.safeParse(rawHeaders);
1200
+ if (result.success) {
1201
+ parsed.headers = result.data;
1202
+ } else {
1203
+ issues.push(...result.error.issues.map((i) => ({
1204
+ path: ["headers", ...i.path.map(String)],
1205
+ message: i.message
1206
+ })));
1207
+ }
1208
+ }
1209
+ if (schemas.body) {
1210
+ if (req.body === null) {
1211
+ issues.push({ path: ["body"], message: "Request body is required" });
1212
+ } else {
1213
+ const bodyText = await req.text();
1214
+ if (!bodyText && req.method !== "GET" && req.method !== "HEAD") {
1215
+ issues.push({ path: ["body"], message: "Request body is required" });
1216
+ } else {
1217
+ let bodyValue;
1218
+ try {
1219
+ bodyValue = JSON.parse(bodyText);
1220
+ } catch {
1221
+ bodyValue = bodyText;
1222
+ }
1223
+ const result = schemas.body.safeParse(bodyValue);
1224
+ if (result.success) {
1225
+ parsed.body = result.data;
1226
+ } else {
1227
+ issues.push(...result.error.issues.map((i) => ({
1228
+ path: ["body", ...i.path.map(String)],
1229
+ message: i.message
1230
+ })));
1231
+ }
1232
+ }
1233
+ }
1234
+ }
1235
+ if (issues.length > 0) {
1236
+ return Response.json({ error: "Validation failed", issues }, { status: 400 });
1237
+ }
1238
+ ctx.parsed = parsed;
1239
+ return next(req, ctx);
1240
+ };
1241
+ }
1242
+
1243
+ // cookie.ts
1244
+ function getCookies(req) {
1245
+ const header = req.headers.get("cookie");
1246
+ if (!header) return {};
1247
+ const cookies = {};
1248
+ for (const pair of header.split(";")) {
1249
+ const idx = pair.indexOf("=");
1250
+ if (idx === -1) continue;
1251
+ const name = pair.slice(0, idx).trim();
1252
+ const value = pair.slice(idx + 1).trim();
1253
+ if (name) {
1254
+ cookies[name] = decodeURIComponent(value);
1255
+ }
1256
+ }
1257
+ return cookies;
1258
+ }
1259
+ function serializeCookie(name, value, options) {
1260
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
1261
+ if (options?.maxAge != null) parts.push(`Max-Age=${options.maxAge}`);
1262
+ if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
1263
+ if (options?.domain) parts.push(`Domain=${options.domain}`);
1264
+ if (options?.path) parts.push(`Path=${options.path}`);
1265
+ if (options?.httpOnly) parts.push("HttpOnly");
1266
+ if (options?.secure) parts.push("Secure");
1267
+ if (options?.sameSite) parts.push(`SameSite=${options.sameSite}`);
1268
+ return parts.join("; ");
1269
+ }
1270
+ function setCookie(res, name, value, options) {
1271
+ const headers = new Headers(res.headers);
1272
+ headers.append("Set-Cookie", serializeCookie(name, value, options));
1273
+ return new Response(res.body, {
1274
+ status: res.status,
1275
+ statusText: res.statusText,
1276
+ headers
1277
+ });
1278
+ }
1279
+ function deleteCookie(res, name, options) {
1280
+ const headers = new Headers(res.headers);
1281
+ headers.append("Set-Cookie", serializeCookie(name, "", { ...options, maxAge: 0 }));
1282
+ return new Response(res.body, {
1283
+ status: res.status,
1284
+ statusText: res.statusText,
1285
+ headers
1286
+ });
1287
+ }
1288
+
1289
+ // upload.ts
1290
+ import { writeFile, mkdir } from "node:fs/promises";
1291
+ import { randomUUID } from "node:crypto";
1292
+ import { join as join2 } from "node:path";
1293
+ function upload(options) {
1294
+ const saveDir = options?.dir;
1295
+ return async (req, ctx, next) => {
1296
+ const ct = req.headers.get("content-type") ?? "";
1297
+ if (!ct.includes("multipart/form-data")) {
1298
+ return next(req, ctx);
1299
+ }
1300
+ const match = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
1301
+ if (!match) {
1302
+ return Response.json({ error: "Missing boundary" }, { status: 400 });
1303
+ }
1304
+ const boundary = match[1] ?? match[2];
1305
+ const body = await req.text();
1306
+ const rawParts = body.split(`--${boundary}`).filter((p) => p && !p.startsWith("--") && !p.startsWith("\r\n--"));
1307
+ const files = {};
1308
+ const fields = {};
1309
+ for (const raw of rawParts) {
1310
+ const trimmed = raw.replace(/^\r?\n/, "");
1311
+ const lines = trimmed.split(/\r?\n/);
1312
+ let i = 0;
1313
+ const headers = {};
1314
+ while (i < lines.length && lines[i].length > 0) {
1315
+ const sep3 = lines[i].indexOf(": ");
1316
+ if (sep3 !== -1) headers[lines[i].slice(0, sep3).toLowerCase()] = lines[i].slice(sep3 + 2);
1317
+ i++;
1318
+ }
1319
+ i++;
1320
+ const bodyValue = lines.slice(i).join("\r\n");
1321
+ const disposition = headers["content-disposition"] ?? "";
1322
+ const nameMatch = disposition.match(/name="([^"]*)"/);
1323
+ if (!nameMatch) continue;
1324
+ const name = nameMatch[1];
1325
+ const filenameMatch = disposition.match(/filename="([^"]*)"/);
1326
+ const filename = filenameMatch?.[1];
1327
+ if (filename) {
1328
+ const buf = Buffer.from(bodyValue.replace(/\r?\n$/, ""), "binary");
1329
+ if (options?.allowedTypes) {
1330
+ const mime = headers["content-type"] ?? "application/octet-stream";
1331
+ if (!options.allowedTypes.includes(mime)) {
1332
+ return Response.json({ error: `File type not allowed: ${mime}` }, { status: 415 });
1333
+ }
1334
+ }
1335
+ if (options?.maxFileSize && buf.byteLength > options.maxFileSize) {
1336
+ return Response.json({ error: `File too large: ${filename}` }, { status: 413 });
1337
+ }
1338
+ const uf = {
1339
+ name: filename,
1340
+ type: headers["content-type"] ?? "application/octet-stream",
1341
+ size: buf.byteLength,
1342
+ buffer: saveDir ? void 0 : buf
1343
+ };
1344
+ if (saveDir) {
1345
+ const filePath = join2(saveDir, `${randomUUID()}-${filename}`);
1346
+ await mkdir(saveDir, { recursive: true });
1347
+ await writeFile(filePath, buf);
1348
+ uf.path = filePath;
1349
+ }
1350
+ if (files[name]) {
1351
+ const existing = files[name];
1352
+ files[name] = Array.isArray(existing) ? [...existing, uf] : [existing, uf];
1353
+ } else {
1354
+ files[name] = uf;
1355
+ }
1356
+ } else {
1357
+ fields[name] = bodyValue.replace(/\r?\n$/, "");
1358
+ }
1359
+ }
1360
+ ctx.parsed = { ...ctx.parsed, files, fields };
1361
+ return next(req, ctx);
1362
+ };
1363
+ }
1364
+
1365
+ // rate-limit.ts
1366
+ function rateLimit(options) {
1367
+ const max = options?.max ?? 100;
1368
+ const window = options?.window ?? 6e4;
1369
+ const getKey = options?.key ?? ((req) => {
1370
+ const forwarded = req.headers.get("x-forwarded-for");
1371
+ if (forwarded) return forwarded.split(",")[0].trim();
1372
+ return new URL(req.url).hostname;
1373
+ });
1374
+ const message = options?.message ?? "Too Many Requests";
1375
+ const hits = /* @__PURE__ */ new Map();
1376
+ const interval = setInterval(() => {
1377
+ const now = Date.now();
1378
+ for (const [key, entry] of hits) {
1379
+ if (entry.reset < now) hits.delete(key);
1380
+ }
1381
+ }, window);
1382
+ if (interval.unref) interval.unref();
1383
+ return async (req, ctx, next) => {
1384
+ const key = getKey(req);
1385
+ const now = Date.now();
1386
+ const entry = hits.get(key);
1387
+ if (!entry || entry.reset < now) {
1388
+ hits.set(key, { count: 1, reset: now + window });
1389
+ const res2 = await next(req, ctx);
1390
+ const headers2 = new Headers(res2.headers);
1391
+ headers2.set("X-RateLimit-Limit", String(max));
1392
+ headers2.set("X-RateLimit-Remaining", String(max - 1));
1393
+ headers2.set("X-RateLimit-Reset", String(Math.ceil((now + window) / 1e3)));
1394
+ return new Response(res2.body, { status: res2.status, statusText: res2.statusText, headers: headers2 });
1395
+ }
1396
+ entry.count++;
1397
+ const remaining = Math.max(0, max - entry.count);
1398
+ if (entry.count > max) {
1399
+ return new Response(message, {
1400
+ status: 429,
1401
+ headers: {
1402
+ "Retry-After": String(Math.ceil((entry.reset - now) / 1e3)),
1403
+ "X-RateLimit-Limit": String(max),
1404
+ "X-RateLimit-Remaining": "0",
1405
+ "X-RateLimit-Reset": String(Math.ceil(entry.reset / 1e3))
1406
+ }
1407
+ });
1408
+ }
1409
+ const res = await next(req, ctx);
1410
+ const headers = new Headers(res.headers);
1411
+ headers.set("X-RateLimit-Limit", String(max));
1412
+ headers.set("X-RateLimit-Remaining", String(remaining));
1413
+ headers.set("X-RateLimit-Reset", String(Math.ceil(entry.reset / 1e3)));
1414
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
1415
+ };
1416
+ }
1417
+
1418
+ // compress.ts
1419
+ import { gzipSync, brotliCompressSync, constants } from "node:zlib";
1420
+ function compress(options) {
1421
+ const level = options?.level ?? 6;
1422
+ const threshold = options?.threshold ?? 1024;
1423
+ return async (req, ctx, next) => {
1424
+ const accept = req.headers.get("accept-encoding") ?? "";
1425
+ const useBrotli = accept.includes("br");
1426
+ const useGzip = !useBrotli && accept.includes("gzip");
1427
+ const useDeflate = !useBrotli && !useGzip && accept.includes("deflate");
1428
+ if (!useBrotli && !useGzip && !useDeflate) {
1429
+ return next(req, ctx);
1430
+ }
1431
+ const res = await next(req, ctx);
1432
+ if (res.status === 304 || res.status === 204 || res.status < 200 || res.status >= 300) {
1433
+ return res;
1434
+ }
1435
+ const ce = res.headers.get("content-encoding");
1436
+ if (ce) return res;
1437
+ const ct = res.headers.get("content-type") ?? "";
1438
+ if (!ct || ct.startsWith("audio/") || ct.startsWith("video/") || ct.startsWith("image/") || ct === "application/zip") {
1439
+ return res;
1440
+ }
1441
+ const body = await res.bytes();
1442
+ if (body.byteLength < threshold) return res;
1443
+ let compressed;
1444
+ let encoding;
1445
+ if (useBrotli) {
1446
+ compressed = brotliCompressSync(body, {
1447
+ params: { [constants.BROTLI_PARAM_QUALITY]: Math.min(level, 11) }
1448
+ });
1449
+ encoding = "br";
1450
+ } else if (useGzip) {
1451
+ compressed = gzipSync(body, { level: Math.min(level, 9) });
1452
+ encoding = "gzip";
1453
+ } else {
1454
+ compressed = gzipSync(body, { level: Math.min(level, 9) });
1455
+ encoding = "deflate";
1456
+ }
1457
+ const headers = new Headers(res.headers);
1458
+ headers.set("Content-Encoding", encoding);
1459
+ headers.set("Content-Length", String(compressed.byteLength));
1460
+ headers.delete("Content-Range");
1461
+ headers.set("Vary", "Accept-Encoding");
1462
+ return new Response(compressed, {
1463
+ status: res.status,
1464
+ statusText: res.statusText,
1465
+ headers
1466
+ });
1467
+ };
1468
+ }
1469
+ export {
1470
+ Router,
1471
+ TsxContext,
1472
+ auth,
1473
+ compress,
1474
+ cors,
1475
+ deleteCookie,
1476
+ getCookies,
1477
+ logger,
1478
+ rateLimit,
1479
+ serve,
1480
+ serveStatic,
1481
+ setCookie,
1482
+ tsx,
1483
+ upload,
1484
+ useTsx,
1485
+ validate
1486
+ };