@server/next 0.21.0 → 0.21.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.21.0",
3
+ "version": "0.21.2",
4
4
  "description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
5
5
  "homepage": "https://server-js.com/",
6
6
  "repository": "https://github.com/franciscop/server-next.git",
@@ -10,8 +10,9 @@
10
10
  "license": "UNLICENSED",
11
11
  "documentation": {
12
12
  "title": "Server JS - A modern web server for Bun and Node.js",
13
+ "home": "./docs/index.html",
13
14
  "menu": {
14
- "About": "/about",
15
+ "About": "/",
15
16
  "Documentation": "/documentation",
16
17
  "Github": "https://github.com/franciscop/server-next"
17
18
  }
package/src/auth/auth.js CHANGED
@@ -48,8 +48,8 @@ export default async function auth(ctx) {
48
48
 
49
49
  const auth = await options.session.get(sessionId);
50
50
  // Mmh, which one to do...
51
- if (!auth) throw ServerError.AUTH_NO_SESSION();
52
- // if (!auth) return; // SESSION ALREADY INVALID; no auth
51
+ // if (!auth) throw ServerError.AUTH_NO_SESSION();
52
+ if (!auth) return; // SESSION ALREADY INVALID; no auth
53
53
 
54
54
  if (!auth.provider) throw ServerError.AUTH_NO_PROVIDER();
55
55
  if (!options.provider.includes(auth.provider)) {
package/src/auth/index.js CHANGED
@@ -12,16 +12,16 @@ const load = async (ctx) => {
12
12
 
13
13
  const middle = async (ctx) => {
14
14
  if (ctx.options.auth) {
15
- ctx.app.post("/auth/logout", logout);
16
-
17
15
  if (ctx.options.auth.provider.includes("github")) {
18
16
  if (!env.GITHUB_ID) throw new Error("GITHUB_ID not defined");
19
17
  if (!env.GITHUB_SECRET) throw new Error("GITHUB_SECRET not defined");
18
+ ctx.app.get("/auth/logout", logout);
20
19
  ctx.app.get("/auth/login/github", providers.github.login);
21
20
  ctx.app.get("/auth/callback/github", providers.github.callback);
22
21
  }
23
22
 
24
23
  if (ctx.options.auth.provider.includes("email")) {
24
+ ctx.app.post("/auth/logout", logout);
25
25
  ctx.app.post("/auth/register/email", providers.email.register);
26
26
  ctx.app.post("/auth/login/email", providers.email.login);
27
27
  ctx.app.put("/auth/password/email", providers.email.password);
@@ -61,7 +61,8 @@ describe("token", () => {
61
61
  it("cannot get the session", async () => {
62
62
  const authorization = "Bearer REqA2l022l8Q0tuI";
63
63
  const req = await api.get("/", { headers: { authorization } });
64
- expect(req).not.toSucceed("Invalid session");
64
+ expect(req.status).toBe(404);
65
+ // expect(req).not.toSucceed("Invalid session");
65
66
  });
66
67
 
67
68
  it("cannot get the user", async () => {
@@ -112,7 +113,8 @@ describe("cookie", () => {
112
113
  it("can get the proper session", async () => {
113
114
  const cookie = "authentication=REqA2l022l8Q0tuI";
114
115
  const req = await api.get("/", { headers: { cookie } });
115
- expect(req).not.toSucceed("Invalid session");
116
+ expect(req.status).toBe(404);
117
+ // expect(req).not.toSucceed("Invalid session");
116
118
  });
117
119
 
118
120
  it("can get the proper session", async () => {
@@ -7,7 +7,7 @@ export default async function logout(ctx) {
7
7
  if (type === "token") {
8
8
  return { token: null };
9
9
  } else if (type === "cookie") {
10
- return cookies({ authorization: null }).send({});
10
+ return cookies({ authorization: null }).redirect("/");
11
11
  } else if (type === "jwt") {
12
12
  throw new Error("JWT auth not supported yet");
13
13
  } else if (type === "key") {
@@ -1,21 +1,9 @@
1
- import middle from "../middle/index.js";
2
1
  import parseResponse from "../parseResponse.js";
3
2
  import pathPattern from "../pathPattern.js";
4
3
  import define from "./define.js";
5
4
  import validate from "./validate.js";
6
5
 
7
- const extendWithDefaults = (ctx) => {
8
- // TODO: find a better way of doing this FFS
9
- // Only want to execute it once; it needs to happen on a per-request
10
- // basis since we only have full access to the options there
11
- if (ctx.app.extended) return;
12
- middle(ctx);
13
- ctx.app.extended = true;
14
- };
15
-
16
6
  export default async function handleRequest(handlers, ctx) {
17
- extendWithDefaults(ctx);
18
-
19
7
  for (let [method, matcher, ...cbs] of handlers[ctx.method]) {
20
8
  const match = pathPattern(matcher, ctx.url.pathname || "/");
21
9
  // Skip this whole middleware if there was no match
package/src/index.js CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  parseHeaders,
12
12
  } from "./helpers/index.js";
13
13
 
14
+ import { assets, auth } from "./middle/index.js";
15
+
14
16
  // Export the reply helpers
15
17
  export * from "./reply.js";
16
18
 
@@ -27,7 +29,7 @@ export default function server(options = {}) {
27
29
  }
28
30
 
29
31
  // Keep a copy of the options in the instance
30
- this.opts = options;
32
+ this.opts = config(options);
31
33
  this.platform = getMachine();
32
34
 
33
35
  // TODO: find a way to remove this hack
@@ -71,6 +73,11 @@ export default function server(options = {}) {
71
73
  if (this.platform.runtime === "node") {
72
74
  this.node();
73
75
  }
76
+
77
+ this.use(assets);
78
+ if (this.opts.auth) {
79
+ this.use(auth({ options: this.opts, app: this }));
80
+ }
74
81
  }
75
82
 
76
83
  server.prototype.self = function () {
@@ -89,12 +96,11 @@ server.prototype.self = function () {
89
96
  // #region Runtimes
90
97
  // Node.js
91
98
  server.prototype.node = async function () {
92
- const options = config(this.opts);
93
99
  const http = await import("http");
94
100
  http
95
101
  .createServer(async (request, response) => {
96
102
  try {
97
- const ctx = await createNodeContext(request, options, this);
103
+ const ctx = await createNodeContext(request, this.opts, this);
98
104
  const out = await handleRequest(this.handlers, ctx);
99
105
 
100
106
  response.writeHead(out.status || 200, parseHeaders(out.headers));
@@ -121,8 +127,7 @@ server.prototype.callback = async function (request, context) {
121
127
  if (typeof Netlify === "undefined") {
122
128
  throw new Error("Netlify doesn't exist");
123
129
  }
124
- const options = config(this.opts);
125
- const ctx = await createWinterContext(request, options, this);
130
+ const ctx = await createWinterContext(request, this.opts, this);
126
131
  return await handleRequest(this.handlers, ctx);
127
132
  } catch (error) {
128
133
  return new Response(error.message, { status: error.status || 500 });
@@ -135,8 +140,7 @@ server.prototype.fetch = async function (request, env) {
135
140
  Object.assign(globalThis.env, env); // Extend env with the passed vars
136
141
 
137
142
  try {
138
- const options = config(this.opts);
139
- const ctx = await createWinterContext(request, options, this);
143
+ const ctx = await createWinterContext(request, this.opts, this);
140
144
  return await handleRequest(this.handlers, ctx);
141
145
  } catch (error) {
142
146
  return new Response(error.message, { status: error.status || 500 });
@@ -146,6 +150,8 @@ server.prototype.fetch = async function (request, env) {
146
150
  // #region HTTP methods
147
151
  // INTERNAL
148
152
  server.prototype.handle = function (method, path, ...middleware) {
153
+ // Do not try to optimize, we NEED the method to remain '*' here so that
154
+ // it doesn't auto-finish
149
155
  if (method === "*") {
150
156
  for (let m in this.handlers) {
151
157
  this.handlers[m].push([method, path, ...middleware]);
@@ -224,17 +230,17 @@ server.prototype.test = function () {
224
230
  );
225
231
 
226
232
  const headers = parseHeaders(res.headers);
227
- let data;
233
+ let body;
228
234
  if (headers["set-cookie"]) {
229
235
  // TODO: this should really be a smart merge of the 2
230
236
  cookie = headers["set-cookie"];
231
237
  }
232
238
  if (headers["content-type"]?.includes("application/json")) {
233
- data = await res.json();
239
+ body = await res.json();
234
240
  } else {
235
- data = await res.text();
241
+ body = await res.text();
236
242
  }
237
- return { status: res.status, headers, data };
243
+ return { status: res.status, headers, body, data: body };
238
244
  };
239
245
  return {
240
246
  app: this,
@@ -1,6 +1,8 @@
1
1
  import { type } from "../reply.js";
2
2
 
3
3
  export default async function assets(ctx) {
4
+ if (!ctx.options.public) return;
5
+ if (!ctx.method !== "get") return;
4
6
  try {
5
7
  // TODO: streaming
6
8
  // Read it as buffer (null)
@@ -1,12 +1,5 @@
1
- import auth from "../auth/index.js";
1
+ import authMod from "../auth/index.js";
2
2
  import assets from "./assets.js";
3
3
 
4
- export default async function middle(ctx) {
5
- // Serve assets
6
- if (ctx.options.public) {
7
- // We need these before other endpoints
8
- ctx.app.handlers.get.unshift(["*", "*", assets]);
9
- }
10
-
11
- await auth.middle(ctx);
12
- }
4
+ const auth = authMod.middle;
5
+ export { assets, auth };
@@ -15,7 +15,7 @@ describe("can route properly", () => {
15
15
  // INTERNAL - so this might change in the future
16
16
  it("has the correct structure", () => {
17
17
  const registeredPaths = app.handlers.get.map((h) => h[1]);
18
- expect(registeredPaths).toEqual(["/hello", "/api/hello", "/"]);
18
+ expect(registeredPaths).toEqual(["*", "/hello", "/api/hello", "/"]);
19
19
  });
20
20
 
21
21
  it("can get fallback when nothing matches", async () => {