weifuwu 0.2.3 → 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 (3) hide show
  1. package/README.md +34 -0
  2. package/dist/index.js +87 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -407,6 +407,40 @@ const app = new Router()
407
407
  serve(app.handler(), { port: 3000 })
408
408
  ```
409
409
 
410
+ ## Graceful shutdown
411
+
412
+ ```ts
413
+ import { serve } from 'weifuwu'
414
+ import type { Server } from 'weifuwu'
415
+
416
+ const ac = new AbortController()
417
+ let server: Server
418
+
419
+ process.on('SIGTERM', () => {
420
+ console.log('shutting down…')
421
+ ac.abort()
422
+ server.stop()
423
+ })
424
+
425
+ server = serve((req, ctx) => new Response('Hello'), {
426
+ port: 3000,
427
+ signal: ac.signal,
428
+ })
429
+ await server.ready
430
+ console.log(`listening on :${server.port}`)
431
+ ```
432
+
433
+ ### Using with WebSocket
434
+
435
+ ```ts
436
+ const app = new Router().ws('/chat', { … })
437
+ const server = serve(app.handler(), {
438
+ port: 3000,
439
+ signal: ac.signal,
440
+ websocket: app.websocketHandler(),
441
+ })
442
+ ```
443
+
410
444
  ## Error handling
411
445
 
412
446
  ```ts
package/dist/index.js CHANGED
@@ -365,7 +365,7 @@ var Router = class _Router {
365
365
  for (let i = 0; i < segments.length; i++) {
366
366
  pathMws.push(...node.pathMws);
367
367
  if (node.wildcard) {
368
- const h = node.handlers.get(method) || node.handlers.get("*");
368
+ const h = node.handlers.get("*") || node.handlers.get(method);
369
369
  if (h) {
370
370
  wildcardHandler = h;
371
371
  wildcardMws = node.middlewares.get(method) || node.middlewares.get("*") || [];
@@ -676,6 +676,20 @@ function scanPages(dir) {
676
676
  layouts,
677
677
  routePath: rPath
678
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
+ }
679
693
  }
680
694
  for (const d of dirs) walk(d);
681
695
  }
@@ -821,26 +835,37 @@ ${scripts.join("\n")}`;
821
835
  async function tsx(options) {
822
836
  const pagesDir = resolve(options.dir);
823
837
  const outDir = join(pagesDir, "..", ".weifuwu", "ssr");
824
- const clientDir = join(pagesDir, "..", ".weifuwu", "client");
825
838
  const pages = scanPages(pagesDir);
826
839
  if (pages.length === 0) return new Router();
827
840
  const allFiles = /* @__PURE__ */ new Set();
828
- const loadMap = /* @__PURE__ */ new Map();
829
- const layoutMap = /* @__PURE__ */ new Map();
830
841
  for (const p of pages) {
831
- allFiles.add(p.entryPath);
832
- if (p.loadPath) {
833
- allFiles.add(p.loadPath);
834
- loadMap.set(p.entryPath, p.loadPath);
835
- }
842
+ if (p.entryPath) allFiles.add(p.entryPath);
843
+ if (p.loadPath) allFiles.add(p.loadPath);
836
844
  for (const lp of p.layouts) allFiles.add(lp);
837
- layoutMap.set(p.entryPath, [...p.layouts]);
838
845
  if (p.routePath) allFiles.add(p.routePath);
839
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
+ }
840
854
  mkdirSync(outDir, { recursive: true });
841
855
  await compileAll([...allFiles], outDir, "node");
842
856
  const router = new Router();
843
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
+ }
844
869
  const url = compiledUrl(p.entryPath, outDir);
845
870
  const mod = await import(url);
846
871
  const Component = mod.default;
@@ -877,6 +902,36 @@ async function tsx(options) {
877
902
  }
878
903
  }
879
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
+ }
880
935
  return router;
881
936
  }
882
937
 
@@ -941,25 +996,36 @@ function cors(options) {
941
996
  function auth(options) {
942
997
  return async (req, ctx, next) => {
943
998
  const headerName = options.header ?? "Authorization";
944
- const header = req.headers.get(headerName);
945
- if (!header) {
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) {
946
1019
  return new Response("Unauthorized", {
947
1020
  status: 401,
948
1021
  headers: headerName.toLowerCase() === "authorization" ? { "WWW-Authenticate": "Bearer" } : void 0
949
1022
  });
950
1023
  }
951
- let token = header;
952
- if (headerName.toLowerCase() === "authorization") {
953
- const parts = header.split(" ");
954
- if (parts[0]?.toLowerCase() === "bearer") {
955
- token = parts.slice(1).join(" ");
956
- }
957
- }
958
1024
  if (options.proxy) {
959
1025
  const proxyUrl = typeof options.proxy === "string" ? new URL(options.proxy) : options.proxy;
960
1026
  const proxyHeaders = {};
961
- if (headerName.toLowerCase() === "authorization") {
962
- proxyHeaders["Authorization"] = header;
1027
+ if (from === "header" && header) {
1028
+ proxyHeaders[headerName] = header;
963
1029
  } else {
964
1030
  proxyUrl.searchParams.set("access_token", token);
965
1031
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weifuwu",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Web-standard HTTP framework for Node.js — (req, ctx) => Response",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",