@server/next 0.25.10 → 0.27.0

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 (63) hide show
  1. package/index.js +2005 -0
  2. package/package.json +17 -13
  3. package/readme.md +1 -0
  4. package/src/{helpers/jsx.js → jsx/jsx-dev-runtime.js} +32 -17
  5. package/src/{helpers/jsx.test.jsx → jsx/jsx-runtime.test.jsx} +1 -1
  6. package/src/ServerError.js +0 -27
  7. package/src/auth/NoSession.js +0 -19
  8. package/src/auth/auth-cookie.test.js +0 -42
  9. package/src/auth/auth-token.test.js +0 -110
  10. package/src/auth/auth.js +0 -62
  11. package/src/auth/index.js +0 -104
  12. package/src/auth/index.test.js +0 -133
  13. package/src/auth/logout.js +0 -20
  14. package/src/auth/providers/email.js +0 -145
  15. package/src/auth/providers/github.js +0 -84
  16. package/src/auth/providers/index.js +0 -4
  17. package/src/auth/session.js +0 -18
  18. package/src/auth/updateUser.js +0 -6
  19. package/src/auth/user.js +0 -9
  20. package/src/context/node.js +0 -68
  21. package/src/context/parseBody.js +0 -107
  22. package/src/context/parseBody.test.js +0 -60
  23. package/src/context/parseCookies.js +0 -9
  24. package/src/context/winter.js +0 -46
  25. package/src/errors/index.js +0 -36
  26. package/src/helpers/StatusError.js +0 -6
  27. package/src/helpers/bucket.js +0 -89
  28. package/src/helpers/bucket.test.js +0 -51
  29. package/src/helpers/color.js +0 -30
  30. package/src/helpers/config.js +0 -64
  31. package/src/helpers/cookies.test.js +0 -23
  32. package/src/helpers/cors.js +0 -24
  33. package/src/helpers/cors.test.js +0 -64
  34. package/src/helpers/createCookies.js +0 -16
  35. package/src/helpers/createId.js +0 -51
  36. package/src/helpers/define.js +0 -18
  37. package/src/helpers/getMachine.js +0 -26
  38. package/src/helpers/handleRequest.js +0 -34
  39. package/src/helpers/index.js +0 -11
  40. package/src/helpers/iterate.js +0 -8
  41. package/src/helpers/parseHeaders.js +0 -15
  42. package/src/helpers/toWeb.js +0 -15
  43. package/src/helpers/types.js +0 -81
  44. package/src/helpers/validate.js +0 -34
  45. package/src/index.d.ts +0 -198
  46. package/src/index.js +0 -268
  47. package/src/index.test.js +0 -87
  48. package/src/index.types.ts +0 -28
  49. package/src/middle/assets.js +0 -17
  50. package/src/middle/assets.test.js +0 -10
  51. package/src/middle/index.js +0 -7
  52. package/src/middle/openapi.js +0 -147
  53. package/src/middle/timer.js +0 -14
  54. package/src/parseResponse.js +0 -111
  55. package/src/pathPattern.js +0 -47
  56. package/src/pathPattern.test.js +0 -99
  57. package/src/polyfill.js +0 -18
  58. package/src/reply.js +0 -153
  59. package/src/router.js +0 -53
  60. package/src/router.test.js +0 -57
  61. package/src/session.test.js +0 -65
  62. package/src/test/toSucceed.js +0 -64
  63. package/src/url.test.js +0 -26
package/src/index.d.ts DELETED
@@ -1,198 +0,0 @@
1
- type Method =
2
- | "socket"
3
- | "get"
4
- | "head"
5
- | "post"
6
- | "put"
7
- | "patch"
8
- | "delete"
9
- | "options";
10
-
11
- type Store = {
12
- get: (key: string) => Promise<any>;
13
- set: (
14
- key: string,
15
- value: any,
16
- { expires }?: { expires?: string | number },
17
- ) => Promise<any>;
18
- };
19
-
20
- type Bucket =
21
- | string
22
- | {
23
- read: (key: string) => ReadableStream;
24
- write: (
25
- key: string,
26
- data:
27
- | string
28
- | NodeJS.ArrayBufferView
29
- | Iterable<string | NodeJS.ArrayBufferView>
30
- | AsyncIterable<string | NodeJS.ArrayBufferView>,
31
- type: BufferEncoding,
32
- ) => Promise<Boolean>;
33
- delete: (key: string) => Promise<Boolean>;
34
- };
35
-
36
- type Auth = {
37
- type: "cookie" | "token";
38
- provider: "github" | "email";
39
- };
40
- type AuthString = `${Auth["type"]}:${Auth["provider"]}`;
41
-
42
- type Domain = `https://${string}/`;
43
- type Origin = boolean | "*" | Domain | Domain[];
44
- type Cors = {
45
- origin: Origin;
46
- methods: string;
47
- headers: string;
48
- credentials?: boolean;
49
- };
50
-
51
- type ServerOptions = {
52
- port?: number;
53
- views?: string | Bucket;
54
- public?: string | Bucket;
55
- uploads?: string | Bucket;
56
- cors?: boolean | Origin | Cors;
57
- auth?: AuthString | Auth;
58
- store?: Store;
59
- };
60
-
61
- type ExtractPathParams<Path extends string> =
62
- Path extends `${string}:${infer Param}(${infer Type})?/${infer Rest}`
63
- ? `${Param}:${Type}?` | ExtractPathParams<`/${Rest}`>
64
- : Path extends `${string}:${infer Param}(${infer Type})?`
65
- ? `${Param}:${Type}?`
66
- : Path extends `${string}:${infer Param}(${infer Type})/${infer Rest}`
67
- ? `${Param}:${Type}` | ExtractPathParams<`/${Rest}`>
68
- : Path extends `${string}:${infer Param}(${infer Type})`
69
- ? `${Param}:${Type}`
70
- : Path extends `${string}:${infer Param}?/${infer Rest}`
71
- ? `${Param}?` | ExtractPathParams<`/${Rest}`>
72
- : Path extends `${string}:${infer Param}?`
73
- ? `${Param}?`
74
- : Path extends `${string}:${infer Param}/${infer Rest}`
75
- ? Param | ExtractPathParams<`/${Rest}`>
76
- : Path extends `${string}:${infer Param}`
77
- ? Param
78
- : never;
79
-
80
- type ParamTypeMap = {
81
- string: string;
82
- number: number;
83
- date: Date;
84
- };
85
-
86
- type InferParamType<T extends string> = T extends keyof ParamTypeMap
87
- ? ParamTypeMap[T]
88
- : string;
89
-
90
- type ParamsToObject<Params extends string> = {
91
- [K in Params as K extends `${infer Key}:${infer Type}?`
92
- ? Key
93
- : K extends `${infer Key}:${infer Type}`
94
- ? Key
95
- : K extends `${infer Key}?`
96
- ? Key
97
- : K]: K extends `${infer Key}:${infer Type}?`
98
- ? InferParamType<Type> | undefined
99
- : K extends `${infer Key}:${infer Type}`
100
- ? InferParamType<Type>
101
- : K extends `${infer Key}?`
102
- ? string | undefined
103
- : string;
104
- };
105
-
106
- type PathToParams<Path extends string> = ParamsToObject<
107
- ExtractPathParams<Path>
108
- >;
109
-
110
- type Simplify<T> = T extends object ? { [K in keyof T]: T[K] } : T;
111
-
112
- type Context<Path extends string = string> = {
113
- method: Method;
114
- headers: { [key: string]: string | string[] };
115
- cookies: { [key: string]: any };
116
- body?: any;
117
- url: URL & {
118
- params: Simplify<PathToParams<Path>>; // Simplify here
119
- query: { [key: string]: string };
120
- };
121
- options: ServerOptions;
122
- };
123
-
124
- type Body = string;
125
-
126
- type ContentType = "application/json" | "text/plain" | (string & {});
127
-
128
- // (src: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
129
- type Headers = {
130
- "cache-control"?: string;
131
- "content-type"?: ContentType;
132
- "set-cookie"?: string;
133
- "content-length"?: string;
134
- server?: string;
135
- location?: string;
136
-
137
- [key: string]: string | undefined;
138
- };
139
-
140
- type InlineReply =
141
- | Response
142
- | { body: Body; headers?: Headers }
143
- | string
144
- | number
145
- | undefined;
146
-
147
- type Middleware<Path extends string = string> = (
148
- ctx: Context<Path>,
149
- ) => InlineReply | void;
150
-
151
- declare interface Router {
152
- get<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
153
- head<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
154
- post<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
155
- put<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
156
- patch<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
157
- del<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
158
- options<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
159
- }
160
-
161
- declare interface Server {
162
- /**
163
- * Launch the server with the optional configuration:
164
- *
165
- * ```js
166
- * export default server({
167
- * port: 3000,
168
- * public: './public',
169
- * store: kv(new Map()),
170
- * })
171
- * ```
172
- *
173
- * **[→ Getting Started](https://react-test.dev/documentation#attr)**
174
- *
175
- * **[→ Options Docs](https://react-test.dev/documentation#attr)**
176
- */
177
- (options?: ServerOptions): this;
178
-
179
- socket(path: string, ...middle: Middleware[]): this;
180
-
181
- get<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
182
- head<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
183
- post<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
184
- put<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
185
- patch<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
186
- del<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
187
- options<Path extends string>(path: Path, ...middle: Middleware<Path>[]): this;
188
-
189
- use(...middle: Middleware[]): this;
190
- router(router: Router): this;
191
- }
192
-
193
- type headers = (obj?: Headers) => any;
194
-
195
- declare const server: Server;
196
- export const router: Router;
197
- export const headers: headers;
198
- export default server;
package/src/index.js DELETED
@@ -1,268 +0,0 @@
1
- import "./polyfill.js";
2
- import "./errors/index.js";
3
-
4
- import createNodeContext from "./context/node.js";
5
- import createWinterContext from "./context/winter.js";
6
- import {
7
- config,
8
- getMachine,
9
- handleRequest,
10
- iterate,
11
- parseHeaders,
12
- } from "./helpers/index.js";
13
-
14
- import { assets, auth, timer, openapi } from "./middle/index.js";
15
-
16
- // Export the reply helpers
17
- export * from "./reply.js";
18
-
19
- export { default as ServerError } from "./ServerError.js";
20
-
21
- // Allow to create a sub-router
22
- export { default as router } from "./router.js";
23
-
24
- export default function server(options = {}) {
25
- // Make it so that the exported one is a prototype of function()
26
- if (!(this instanceof server)) {
27
- return new server(options).self();
28
- }
29
-
30
- // Keep a copy of the options in the instance
31
- this.opts = config(options);
32
- this.platform = getMachine();
33
-
34
- // TODO: find a way to remove this hack
35
- this.extended = false;
36
-
37
- // Skip "forbidden methods" https://fetch.spec.whatwg.org/#concept-method
38
- this.handlers = {
39
- socket: [],
40
- get: [],
41
- head: [],
42
- post: [],
43
- put: [],
44
- patch: [],
45
- delete: [],
46
- options: [],
47
- };
48
-
49
- this.sockets = [];
50
- // Note: required by Bun
51
- this.websocket = {
52
- message: async (socket, body) => {
53
- this.handlers.socket
54
- ?.filter((s) => s[1] === "message")
55
- ?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
56
- },
57
- open: (ws) => {
58
- this.sockets.push(ws);
59
- this.handlers.socket
60
- ?.filter((s) => s[1] === "open")
61
- ?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
62
- },
63
- close: (ws) => {
64
- this.sockets.splice(this.sockets.indexOf(ws), 1);
65
- this.handlers.socket
66
- ?.filter((s) => s[1] === "close")
67
- ?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
68
- },
69
- };
70
-
71
- // Initialize it right away for Node.js
72
- if (this.platform.runtime === "node") {
73
- this.node();
74
- }
75
-
76
- this.use(timer);
77
- this.use(assets);
78
- if (this.opts.openapi) {
79
- const path = this.opts.openapi.path || "/docs";
80
- this.get(path, openapi);
81
- }
82
- if (this.opts.auth) {
83
- this.use(auth({ options: this.opts, app: this }));
84
- }
85
- }
86
-
87
- server.prototype.self = function () {
88
- const cb = this.callback.bind(this);
89
- const proto = Object.getPrototypeOf(this);
90
- for (const key in { ...proto, ...this }) {
91
- if (typeof this[key] === "function") {
92
- cb[key] = this[key].bind(this);
93
- } else {
94
- cb[key] = this[key];
95
- }
96
- }
97
- return cb;
98
- };
99
-
100
- // #region Runtimes
101
- // Node.js
102
- server.prototype.node = async function () {
103
- const http = await import("node:http");
104
- http
105
- .createServer(async (request, response) => {
106
- try {
107
- const ctx = await createNodeContext(request, this);
108
- const out = await handleRequest(this.handlers, ctx);
109
-
110
- response.writeHead(out.status || 200, parseHeaders(out.headers));
111
- if (out.body instanceof ReadableStream) {
112
- await iterate(out.body, (chunk) => response.write(chunk));
113
- } else {
114
- response.write(out.body || "");
115
- }
116
- response.end();
117
- } catch (error) {
118
- response.writeHead(error.status || 500);
119
- response.write(error.message || "");
120
- response.end();
121
- }
122
- })
123
- .listen(this.opts.port);
124
- };
125
-
126
- // Netlify
127
- server.prototype.callback = async function (request, context) {
128
- // Consider simply renaming to "ctx.next()"
129
- request.context = context;
130
- try {
131
- if (typeof Netlify === "undefined") {
132
- throw new Error("Netlify doesn't exist");
133
- }
134
- const ctx = await createWinterContext(request, this);
135
- return await handleRequest(this.handlers, ctx);
136
- } catch (error) {
137
- return new Response(error.message, { status: error.status || 500 });
138
- }
139
- };
140
-
141
- // WinterCG, Bun, Cloudflare Workers
142
- server.prototype.fetch = async function (request, env) {
143
- if (env?.upgrade(request)) return;
144
- Object.assign(globalThis.env, env); // Extend env with the passed vars
145
-
146
- let ctx;
147
- let res;
148
- let error;
149
- try {
150
- ctx = await createWinterContext(request, this);
151
- res = await handleRequest(this.handlers, ctx);
152
- } catch (err) {
153
- error = err;
154
- res = new Response(error.message, { status: error.status || 500 });
155
- }
156
- ctx?.unstableFire("finish", { ...ctx, error, res, end: performance.now() });
157
- return res;
158
- };
159
-
160
- // #region HTTP methods
161
- // INTERNAL
162
- server.prototype.handle = function (method, path, ...middleware) {
163
- // Do not try to optimize, we NEED the method to remain '*' here so that
164
- // it doesn't auto-finish
165
- if (method === "*") {
166
- for (const m in this.handlers) {
167
- this.handlers[m].push([method, path, ...middleware]);
168
- }
169
- } else {
170
- this.handlers[method].push([method, path, ...middleware]);
171
- }
172
-
173
- return this.self();
174
- };
175
-
176
- server.prototype.socket = function (path, ...middleware) {
177
- return this.handle("socket", path, ...middleware);
178
- };
179
-
180
- server.prototype.get = function (path, ...middleware) {
181
- return this.handle("get", path, ...middleware);
182
- };
183
-
184
- server.prototype.head = function (path, ...middleware) {
185
- return this.handle("head", path, ...middleware);
186
- };
187
-
188
- server.prototype.post = function (path, ...middleware) {
189
- return this.handle("post", path, ...middleware);
190
- };
191
-
192
- server.prototype.put = function (path, ...middleware) {
193
- return this.handle("put", path, ...middleware);
194
- };
195
-
196
- server.prototype.patch = function (path, ...middleware) {
197
- return this.handle("patch", path, ...middleware);
198
- };
199
-
200
- server.prototype.del = function (path, ...middleware) {
201
- return this.handle("delete", path, ...middleware);
202
- };
203
-
204
- server.prototype.options = function (path, ...middleware) {
205
- return this.handle("options", path, ...middleware);
206
- };
207
-
208
- server.prototype.use = function (...middleware) {
209
- if (typeof middleware[0] === "string") {
210
- return this.handle("*", ...middleware);
211
- }
212
- return this.handle("*", "*", ...middleware);
213
- };
214
-
215
- // Unwind the children routers into the main router
216
- server.prototype.router = function (basePath, router) {
217
- basePath = `/${basePath}/`.replace(/^\/+/, "/").replace(/\/+$/, "/");
218
- for (const m in router.handlers) {
219
- for (const [method, path, ...callbacks] of router.handlers[m]) {
220
- this.handle(method, basePath + path.replace(/^\//, ""), ...callbacks);
221
- }
222
- }
223
- return this.self();
224
- };
225
-
226
- // #region Testing helper
227
- server.prototype.test = function () {
228
- let cookie = "";
229
- const fetch = async (path, options = {}) => {
230
- if (!options.headers) options.headers = {};
231
- if (options.body && typeof options.body !== "string") {
232
- options.headers["content-type"] = "application/json";
233
- options.body = JSON.stringify(options.body);
234
- }
235
- if (cookie && !options.headers.cookie) {
236
- options.headers.cookie = cookie;
237
- }
238
- const res = await this.fetch(
239
- new Request(`http://localhost:3000${path}`, options),
240
- );
241
-
242
- const headers = parseHeaders(res.headers);
243
- let body;
244
- if (headers["set-cookie"]) {
245
- // TODO: this should really be a smart merge of the 2
246
- cookie = headers["set-cookie"];
247
- }
248
- if (headers["content-type"]?.includes("application/json")) {
249
- body = await res.json();
250
- } else {
251
- body = await res.text();
252
- }
253
- return { status: res.status, headers, body };
254
- };
255
- return {
256
- app: this,
257
- get: (path, options) => fetch(path, { method: "get", ...options }),
258
- head: (path, options) => fetch(path, { method: "head", ...options }),
259
- post: (path, body, options) =>
260
- fetch(path, { method: "post", body, ...options }),
261
- put: (path, body, options) =>
262
- fetch(path, { method: "put", body, ...options }),
263
- patch: (path, body, options) =>
264
- fetch(path, { method: "patch", body, ...options }),
265
- delete: (path, options) => fetch(path, { method: "delete", ...options }),
266
- options: (path, options) => fetch(path, { method: "options", ...options }),
267
- };
268
- };
package/src/index.test.js DELETED
@@ -1,87 +0,0 @@
1
- import "./test/toSucceed.js";
2
-
3
- import server, { status } from "./index.js";
4
-
5
- describe("exports", () => {
6
- it("exports as a function", () => {
7
- expect(typeof server()).toBe("function");
8
- });
9
-
10
- it("nested is also a function", () => {
11
- expect(typeof server().get("/", () => {})).toBe("function");
12
- });
13
-
14
- it("export has a fetch", () => {
15
- expect(typeof server().fetch).toBe("function");
16
- expect(typeof server().get().fetch).toBe("function");
17
- });
18
-
19
- it("export has the basic methods", () => {
20
- expect(typeof server().get).toBe("function");
21
- expect(typeof server().post).toBe("function");
22
- expect(typeof server().use).toBe("function");
23
- expect(typeof server().router).toBe("function");
24
- });
25
-
26
- it("export has the basic nested methods", () => {
27
- expect(typeof server().get().get).toBe("function");
28
- expect(typeof server().post().post).toBe("function");
29
- expect(typeof server().use().use).toBe("function");
30
- expect(typeof server().get().router).toBe("function");
31
- });
32
-
33
- it("nested is also a function", () => {
34
- expect(typeof server().get("/", () => {}).fetch).toBe("function");
35
- });
36
- });
37
-
38
- describe("return different types", () => {
39
- const api = server()
40
- .get("/", () => "Hello world")
41
- .get("/text", () => "Hello world")
42
- .get("/array", () => ["Hello world"])
43
- .get("/object", () => ({ hello: "world" }))
44
- .get("/status", () => 201)
45
- .test();
46
-
47
- it("can get the plain text", async () => {
48
- const { body } = await api.get("/text");
49
- expect(body).toBe("Hello world");
50
- });
51
-
52
- it("can get the array", async () => {
53
- const { body } = await api.get("/array");
54
- expect(body).toEqual(["Hello world"]);
55
- });
56
-
57
- it("can get the object", async () => {
58
- const req = await api.get("/object");
59
- expect(req).toSucceed({ hello: "world" });
60
- });
61
-
62
- it("can get the status", async () => {
63
- const req = await api.get("/status");
64
- expect(req).toSucceed();
65
- expect(req.status).toBe(201);
66
- });
67
- });
68
-
69
- describe("simple post works", () => {
70
- const api = server()
71
- .post("/", (ctx) => status(201).send(ctx.body))
72
- .test();
73
-
74
- it("can post new data", async () => {
75
- const { body, status, headers } = await api.post("/", "New Data");
76
- expect(status).toBe(201);
77
- expect(body).toBe("New Data");
78
- expect(headers["content-type"]).toBe("text/plain");
79
- });
80
-
81
- it("will return JSON", async () => {
82
- const { body, status, headers } = await api.post("/", { hello: "world" });
83
- expect(status).toBe(201);
84
- expect(body).toEqual({ hello: "world" });
85
- expect(headers["content-type"]).toBe("application/json");
86
- });
87
- });
@@ -1,28 +0,0 @@
1
- import server, { headers, router } from ".";
2
-
3
- const users = router
4
- .post("/users", (ctx) => {
5
- console.log(ctx.url.pathname);
6
- console.log(ctx.url.query);
7
- console.log(ctx.url.params); // No .id
8
- })
9
- .get("/users/:id?", (ctx) => {
10
- console.log(ctx.url.params.id);
11
- })
12
- .del("/users/:id", (ctx) => {
13
- console.log(ctx.url.params.id);
14
- });
15
-
16
- server()
17
- .get("/", (ctx) => {
18
- console.log(ctx.method);
19
- console.log(ctx.url);
20
- console.log(ctx.headers);
21
- console.log(ctx.cookies);
22
- console.log(ctx.body);
23
- return headers();
24
- })
25
- .router(users)
26
- .post("/", () => {
27
- return 201;
28
- });
@@ -1,17 +0,0 @@
1
- import { type } from "../reply.js";
2
-
3
- export default async function assets(ctx) {
4
- if (!ctx.options.public) return;
5
- if (ctx.method !== "get") return;
6
- // The homepage _cannot_ be a file by definition. We could consider sending
7
- // `index.html`, but that's easy with `.get('/', () => file('index.html'))
8
- if (ctx.url.pathname === "/") return;
9
- try {
10
- // TODO: streaming
11
- const asset = await ctx.options.public.read(ctx.url.pathname, null);
12
- if (!asset) return;
13
- return type(ctx.url.pathname.split(".").pop()).send(asset);
14
- } catch (error) {
15
- // NO-OP; if there's no file, keep going the normal flow
16
- }
17
- }
@@ -1,10 +0,0 @@
1
- import server from "../";
2
-
3
- describe("static assets", () => {
4
- it("can serve a simple file", async () => {
5
- const app = server({ public: "./src/middle/" }).test();
6
- const res = await app.get("/assets.test.js");
7
- expect(res.body).toInclude("describe");
8
- expect(res.headers["content-type"]).toBe("text/javascript");
9
- });
10
- });
@@ -1,7 +0,0 @@
1
- import authMod from "../auth/index.js";
2
- import assets from "./assets.js";
3
- import timer from "./timer.js";
4
- import openapi from "./openapi.js";
5
-
6
- const auth = authMod.middle;
7
- export { assets, auth, timer, openapi };